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

PrimitiveComparison: Retain type arguments if present #39

Merged
merged 4 commits into from
Apr 11, 2022

Conversation

rickie
Copy link
Member

@rickie rickie commented Feb 9, 2022

No description provided.

@rickie rickie requested review from Stephan202 and werli February 9, 2022 08:54
@@ -37,7 +37,6 @@
*/
// XXX: Add more documentation. Explain how this is useful in the face of refactoring to more
// specific types.
// XXX: Change this checker's name?
Copy link
Member Author

Choose a reason for hiding this comment

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

Did not improve the naming, but suggest that we either improve the naming or delete this XXX 😄.

Copy link
Member

Choose a reason for hiding this comment

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

Or keep the comment since it may still apply 🙃

Copy link
Member Author

Choose a reason for hiding this comment

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

To shortly clarify, I did think about the XXX but think that the name is fine as it is.
So I removed the line to "start the discussion" because I feel that we can easily do the XXX by either removing it or coming up with an improvement 😄.

Some ideas: ComparatorUsage, ComparatorPrimitive, CanonicalComparatorUsage.

WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

ComparatorUsage and CanonicalComparatorUsage are nice, but hint at something more generic. Let's keep the current name, at least in the context of the current PR.

@@ -77,23 +76,44 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState
}

return getPotentiallyBoxedReturnType(tree.getArguments().get(0))
.flatMap(cmpType -> tryFix(tree, state, cmpType, isStatic))
.flatMap(cmpType -> tryMakeMethodCallMorePrecise(tree, cmpType, isStatic, state))
Copy link
Member Author

Choose a reason for hiding this comment

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

Not entirely sure about the method name, but figured that we could be more clear than tryFix here.

@@ -128,9 +148,6 @@ private static String getPreferredMethod(VisitorState state, Type cmpType, boole
}
}

// XXX: We drop explicitly specified generic type information. In case the number of type
// arguments before and after doesn't match, that's for the better. But if we e.g. replace
// `comparingLong` with `comparingInt`, then we should retain it.
Copy link
Member Author

Choose a reason for hiding this comment

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

The tests don't show such a replacement. Is this a realistic scenario that we (will) handle with this check?

Copy link
Member

@werli werli left a comment

Choose a reason for hiding this comment

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

The tests convince me to approve, but some minor things are to improve here.

@@ -37,7 +37,6 @@
*/
// XXX: Add more documentation. Explain how this is useful in the face of refactoring to more
// specific types.
// XXX: Change this checker's name?
Copy link
Member

Choose a reason for hiding this comment

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

Or keep the comment since it may still apply 🙃

Comment on lines 85 to 87
private static Optional<Fix> tryFix(
MethodInvocationTree tree, VisitorState state, Type cmpType, boolean isStatic) {
private static Optional<Fix> tryMakeMethodCallMorePrecise(
MethodInvocationTree tree, Type cmpType, boolean isStatic, VisitorState state) {
Copy link
Member

Choose a reason for hiding this comment

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

(1) Why did we change the order? Isn't the VisitorState much more important? Here and in the other methods.
(2) What was this renamed? Don't we still try to fix it?

Copy link
Member Author

Choose a reason for hiding this comment

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

(1) It is sort of an "unwritten rule/convention". In both Error Prone and error-prone-support the VisitorState always comes last.

(2) IMO we can use the method name to better express what we are trying to do. The PrimitiveComparisonCheck tries to improve multiple things and by changing the method name, we can make it more clear which part we are trying to fix in this method.

Copy link
Member

Choose a reason for hiding this comment

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

(1) ✔️
(2) Not sure whether tryMakeMethodCallMorePrecise is expressing anything well 🙃 But I also don't have a better suggestion right now. 🙂

.map(preferredMethodName -> suggestFix(tree, preferredMethodName, state));
}

private static String getPreferredMethod(VisitorState state, Type cmpType, boolean isStatic) {
private static String prefixWithTypeArgumentsIfNeeded(
Copy link
Member

Choose a reason for hiding this comment

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

The "if needed" part looks off to me. WDYT?

Suggested change
private static String prefixWithTypeArgumentsIfNeeded(
private static String mayPrefixWithTypeArguments(

Comment on lines 107 to 113
String optionalSecondTypeArgument =
methodNameIsComparing ? ", " + cmpType.tsym.getSimpleName() : "";
return String.format(
"<%s%s>%s",
Util.treeToString(tree.getTypeArguments().get(0), state),
optionalSecondTypeArgument,
preferredMethodName);
Copy link
Member

Choose a reason for hiding this comment

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

This part feels pretty hacky. Why can't we join both potential type arguments?

    String typeArgument =
        Stream.concat(
                Stream.of(Util.treeToString(tree.getTypeArguments().get(0), state)),
                Stream.of(cmpType.tsym.getSimpleName()).filter(u -> methodNameIsComparing))
            .collect(joining(","));

    return String.format("<%s>%s", typeArgument, preferredMethodName);

@rickie rickie added this to the 0.1.0 milestone Apr 4, 2022
@Stephan202 Stephan202 force-pushed the rossendrijver/primitive_comparison_type_arguments branch from 9f7ddfa to 2ef98c3 Compare April 9, 2022 13:05
Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

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

Rebased and added one more commit. Suggested commit message:

Have `PrimitiveComparisonCheck` retain relevant generic type parameters (#39)

Comment on lines 85 to 86
private static Optional<Fix> tryFix(
MethodInvocationTree tree, VisitorState state, Type cmpType, boolean isStatic) {
private static Optional<Fix> tryMakeMethodCallMorePrecise(
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure what it means to make a method call "more precise". IMHO the old name was fine, but how about attemptMethodInvocationReplacement?

(That said, tryFix is also used in other checks.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I like attemptMethodInvocationReplacement. I can see why tryFix in the context of a specific check could make sense, but I feel that (e.g.) the name attemptMethodInvocationReplacement makes it clearer than the original name.

.map(preferredMethodName -> suggestFix(tree, preferredMethodName, state));
}

private static String getPreferredMethod(VisitorState state, Type cmpType, boolean isStatic) {
private static String mayPrefixWithTypeArguments(
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 expect a method named mayX to return a boolean. How about e.g. prefixTypeArgumentsIfRelevant?

private static String getPreferredMethod(VisitorState state, Type cmpType, boolean isStatic) {
private static String mayPrefixWithTypeArguments(
String preferredMethodName, MethodInvocationTree tree, Type cmpType, VisitorState state) {
int typeArgumentsCount = tree.getTypeArguments().size();
Copy link
Member

Choose a reason for hiding this comment

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

In English "count" is preceded by the singular of whatever is counted.

Suggested change
int typeArgumentsCount = tree.getTypeArguments().size();
int typeArgumentCount = tree.getTypeArguments().size();

Stream.concat(
Stream.of(Util.treeToString(tree.getTypeArguments().get(0), state)),
Stream.of(cmpType.tsym.getSimpleName()).filter(u -> methodNameIsComparing))
.collect(joining(","));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.collect(joining(","));
.collect(joining(", "));

That said, we can drop the String.format below if here we do

Suggested change
.collect(joining(","));
.collect(joining(", ", "<", ">"));

int typeArgumentsCount = tree.getTypeArguments().size();
boolean methodNameIsComparing = "comparing".equals(preferredMethodName);

if (typeArgumentsCount == 0 || (typeArgumentsCount == 1 && !methodNameIsComparing)) {
Copy link
Member

Choose a reason for hiding this comment

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

The tests also pass if I simplify this to:

Suggested change
if (typeArgumentsCount == 0 || (typeArgumentsCount == 1 && !methodNameIsComparing)) {
if (typeArgumentsCount == 0) {

I think we should add additional tests to demonstrate in which case the added complexity is necessary. IIUC the check can then become:

Suggested change
if (typeArgumentsCount == 0 || (typeArgumentsCount == 1 && !methodNameIsComparing)) {
if (tree.getTypeArguments().isEmpty() || preferredMethodName.startsWith("then")) {

Comment on lines 460 to 466
" Comparator<A> bCmp = Comparator.<A>comparingInt(o -> (byte) 0);",
" Comparator<A> cCmp = Comparator.<A>comparingInt(o -> (char) 0);",
" Comparator<A> sCmp = Comparator.<A>comparingInt(o -> (short) 0);",
" Comparator<A> iCmp = Comparator.<A>comparingInt(o -> 0);",
" Comparator<A> lCmp = Comparator.<A>comparingLong(o -> 0L);",
" Comparator<A> fCmp = Comparator.<A>comparingDouble(o -> 0.0f);",
" Comparator<A> dCmp = Comparator.<A>comparingDouble(o -> 0.0);",
Copy link
Member

Choose a reason for hiding this comment

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

We should also test the cases where (in a non-static import context) absent type information stays absent.

@@ -37,7 +37,6 @@
*/
// XXX: Add more documentation. Explain how this is useful in the face of refactoring to more
// specific types.
// XXX: Change this checker's name?
Copy link
Member

Choose a reason for hiding this comment

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

ComparatorUsage and CanonicalComparatorUsage are nice, but hint at something more generic. Let's keep the current name, at least in the context of the current PR.

@rickie rickie force-pushed the rossendrijver/primitive_comparison_type_arguments branch from 2ef98c3 to c873787 Compare April 11, 2022 13:47
@rickie
Copy link
Member Author

rickie commented Apr 11, 2022

Rebased. Changes look good!

@Stephan202 Stephan202 merged commit e8d9221 into master Apr 11, 2022
@Stephan202 Stephan202 deleted the rossendrijver/primitive_comparison_type_arguments branch April 11, 2022 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants