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

Have InvalidParam support records #35

Closed
wants to merge 1 commit into from

Conversation

rickie
Copy link
Member

@rickie rickie commented Jul 18, 2022

Resolves google#2321.

@rickie rickie requested a review from Stephan202 July 18, 2022 08:00
Copy link
Member Author

@rickie rickie left a comment

Choose a reason for hiding this comment

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

Some context.

@@ -101,6 +110,15 @@ public Description matchMethod(MethodTree methodTree, VisitorState state) {
return Description.NO_MATCH;
}

private static MethodTree getGeneratedConstructor(ClassTree classTree) {
return classTree.getMembers().stream()
Copy link
Member Author

Choose a reason for hiding this comment

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

For records in some cases the generated constructor could not be found by using ASTHelpers#isGeneratedConstructor. That's why it's implemented with the contentEquals("<init>") check.

Perhaps that is a bigger issue we should solve. However, I think that is out of scope for this PR.

.map(MethodTree.class::cast)
.findFirst()
.filter(tree -> tree.getName().contentEquals("<init>"))
.orElseThrow(() -> new IllegalStateException("Records must have a generated ctor"));
Copy link
Member Author

Choose a reason for hiding this comment

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

This error message could be better.

@rickie rickie force-pushed the rossendrijver/invalidparam_records branch from 625a631 to b565ccc Compare July 18, 2022 08:06
@@ -798,6 +798,13 @@ private static boolean isFinal(Symbol symbol) {
return (symbol.flags() & Flags.FINAL) == Flags.FINAL;
}

private static final long RECORD_FLAG = 1L << 61;
Copy link
Member Author

@rickie rickie Jul 18, 2022

Choose a reason for hiding this comment

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

We can't use Flags.RECORD yet, because that would fail with JDK 11...

@rickie rickie force-pushed the rossendrijver/invalidparam_records branch from b565ccc to 2455ece Compare July 18, 2022 08:49
Copy link

@dhoepelman dhoepelman left a comment

Choose a reason for hiding this comment

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

Any timeline on merging this? 😇 We're peppering our code with @SupressWarning("InvalidParam") at the moment.

@@ -798,6 +798,13 @@ private static boolean isFinal(Symbol symbol) {
return (symbol.flags() & Flags.FINAL) == Flags.FINAL;
}

private static final long RECORD_FLAG = 1L << 61;

Choose a reason for hiding this comment

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

Suggested change
private static final long RECORD_FLAG = 1L << 61;
/**
* Flag for a record class, its canonical constructor and components. Can be replaced by
* com.sun.tools.javac.code.Flags.RECORD once support minimum JDK version is 14.
*/
private static final long RECORD_FLAG = 1L << 61;

Copy link
Member Author

Choose a reason for hiding this comment

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

Although I really like the suggestion, I used this one-liner because it's used in the Error Prone project twice like that and wanted to be consistent.

So I'd lean towards leaving it as-is. (When we use this in error-prone-support we should definitely use this as Javadoc.

Copy link
Member Author

@rickie rickie Aug 18, 2022

Choose a reason for hiding this comment

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

Oh I misread, this is in the ASTHelpers of course... In that case, it is a good idea to add this. Will commit that.

@rickie rickie force-pushed the rossendrijver/invalidparam_records branch from 2455ece to 341454f Compare August 18, 2022 09:56
@@ -807,6 +807,17 @@ private static boolean isFinal(Symbol symbol) {
return (symbol.flags() & Flags.FINAL) == Flags.FINAL;
}

/**
* Flag for a record class, its canonical constructor and components. Can be replaced by {@code
Copy link
Member

Choose a reason for hiding this comment

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

W.r.t. components: not sure what is meant by that, but based on some tests with the debugger this does not include members. (The original documentation talks about the "state vector"; see below.) Maybe we should leave this out?

Copy link
Member

Choose a reason for hiding this comment

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

Upon further debugging, I found that a subset of record members are flagged. I now better understand what is meant, so will update the documentation.

Comment on lines 816 to 831
/** Returns true if the given {@link Symbol} is a record. */
public static boolean isRecord(Symbol symbol) {
return (symbol.flags() & RECORD_FLAG) == RECORD_FLAG;
}
Copy link
Member

Choose a reason for hiding this comment

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

The JDK 17 Javadoc for com.sun.tools.javac.code.Flags.RECORD says:

    /**
     * Flag to indicate that a class is a record. The flag is also used to mark fields that are
     * part of the state vector of a record and to mark the canonical constructor
     */

Not sure what the state vector is, but tests indicate that this method also returns true for the canonical constructor. We should either fix that or update the documentation.

Copy link
Member

Choose a reason for hiding this comment

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

^ Okay, IIUC the state vector comprises the fields that are populated by the canonical constructor.

@@ -74,11 +75,19 @@ public final class InvalidParam extends BugChecker implements ClassTreeMatcher,
public Description matchClass(ClassTree classTree, VisitorState state) {
DocTreePath path = getDocTreePath(state);
if (path != null) {
ImmutableSet<String> parameters = ImmutableSet.of();
// XXX: `isRecord`... should it also be able to handle `Tree`s?
Copy link
Member

Choose a reason for hiding this comment

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

👀

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we should defer it for now. We might want to improve this in a follow-up PR where we look at record handling in general.

@Stephan202 Stephan202 force-pushed the rossendrijver/invalidparam_records branch from 1e941a9 to 4953d01 Compare September 7, 2022 15: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 a commit.

Suggested commit message:

Have `InvalidParam` support records 

Fixes #2321.

@@ -101,6 +110,15 @@ public Description matchMethod(MethodTree methodTree, VisitorState state) {
return Description.NO_MATCH;
}

private static MethodTree getGeneratedConstructor(ClassTree classTree) {
Copy link
Member

Choose a reason for hiding this comment

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

Given the current implementation:

Suggested change
private static MethodTree getGeneratedConstructor(ClassTree classTree) {
private static MethodTree getFirstConstructor(ClassTree classTree) {

This is not necessarily a generated constructor; see e.g. the normalConstructor_record test case.

That said, what we what, IIUC, is:

Suggested change
private static MethodTree getGeneratedConstructor(ClassTree classTree) {
private static MethodTree getCanonicalRecordConstructor(ClassTree classTree) {

But this does raise the question: can we reliable assume that the first constructor is always the canonical constructor? It might be better to explicitly test against #isRecord; will push a proposal.

@@ -807,6 +807,17 @@ private static boolean isFinal(Symbol symbol) {
return (symbol.flags() & Flags.FINAL) == Flags.FINAL;
}

/**
* Flag for a record class, its canonical constructor and components. Can be replaced by {@code
Copy link
Member

Choose a reason for hiding this comment

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

Upon further debugging, I found that a subset of record members are flagged. I now better understand what is meant, so will update the documentation.

Comment on lines 816 to 831
/** Returns true if the given {@link Symbol} is a record. */
public static boolean isRecord(Symbol symbol) {
return (symbol.flags() & RECORD_FLAG) == RECORD_FLAG;
}
Copy link
Member

Choose a reason for hiding this comment

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

^ Okay, IIUC the state vector comprises the fields that are populated by the canonical constructor.

ImmutableSet<String> parameters =
ASTHelpers.isRecord(ASTHelpers.getSymbol(classTree))
? getGeneratedConstructor(classTree).getParameters().stream()
.map(v -> v.getName().toString())
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
.map(v -> v.getName().toString())
.map(p -> p.getName().toString())

@rickie rickie force-pushed the rossendrijver/invalidparam_records branch from 4953d01 to 16759a4 Compare September 8, 2022 08:08
@rickie rickie changed the title Support records in InvalidParam Have InvalidParam support records Sep 8, 2022
@rickie
Copy link
Member Author

rickie commented Sep 8, 2022

Filed google#3437.

@rickie rickie force-pushed the rossendrijver/invalidparam_records branch from 16759a4 to 2ccb778 Compare September 26, 2022 10:04
@rickie rickie force-pushed the rossendrijver/invalidparam_records branch from 2ccb778 to bc2b5da Compare November 25, 2022 13:31
@rickie rickie force-pushed the rossendrijver/invalidparam_records branch from bc2b5da to 128ca4d Compare December 13, 2022 14:36
@rickie
Copy link
Member Author

rickie commented Dec 29, 2022

Fixed in google#2321.

@rickie rickie closed this Dec 29, 2022
@Stephan202 Stephan202 deleted the rossendrijver/invalidparam_records branch December 30, 2022 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants