-
-
Notifications
You must be signed in to change notification settings - Fork 355
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
feat(record): add Java 16 record support #4134
Conversation
a934e5c
to
8310ad3
Compare
component.setParent(this); | ||
getFactory().getEnvironment().getModelChangeListener().onSetAdd(this, CtRole.RECORD_COMPONENT, components, component); | ||
components.add(component); | ||
if (!getMethods().contains(component.toMethod())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure of this, but will this not fail if a predefined accessor exists with a different body than the one generated with toMethod?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this condition returns true when another accessor for the same field exists, there may be problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch should be fixed now
To make sure we don't forget about this, I suggest you add an empty commit with this commit message:
GitHub should then automatically add them as coauthors when we merge the PR. |
I have added the missing text to the initial post. The commit history is a bit convoluted, but as there was already feedback, we should avoid cleaning it for now. |
Hi @MartinWitt, thanks for putting in all this work! I haven't had the time to review the source code in any particular detail, but I can answer your open questions so you can finish the PR.
I agree, let's not generate those members unless we find a very compelling use case for doing so. I can think of none.
Sounds like a good idea to me. In general, I don't really like the fact that
tl;dr: Remove the check for if the element is a constructor and see what happens. Implicit elements should not be printed. If we ignore the inconsistent use of the
Yeah, the version of SonarJava used is not reliable on dead stores. Overall, we've had way too many false positives with that version of SonarJava, so I'm removing it for the time being (#4146). |
After some thought process, I believe a good solution is: public class JLSCorrectnessException extends SpoonException() and add a new method to protected void throwJLSCorrectnesException(String reason) {
if(!this.getFactory().getEnvironment().ignoreJLSCorrectness()) {
throw new JLSCorrectnessException(reason);
}
else {
.... logging?
} This flag allows writing code transformations, which produce JLS invalid code, in intermediate steps or at the end. But if a user wants JLS correct code, he can set the flag to true. |
I removed the review tag because we(@I-Al-Istannen, @SirYwell and I) implemented shadow records and it's not 100% done |
I changed it back to review, but #4148 is still a stopper. In the latest update, the |
Co-authored-by: I-Al-Istannen <[email protected]> Co-authored-by: Hannes Greule <[email protected]>
This pull request introduces 1 alert when merging 77fcfb0 into d9bcb11 - view on LGTM.com new alerts:
|
ping @monperrus, don't forget about this substantial contribution ping @MartinWitt, some trivial conflicts to resolve |
yes, it's on my todo list, once I'm done with fixing the bulk of Java 11 build / CI errors. Plus, I really look forward to the next major release with Java 16 support. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @MartinWitt,
Made a pass over this important PR. It's really good.
- Left one question about a test change
- There is a conflict to fix
- FYI I made a couple of formatting changes on the fly
After, I'm happy to merge.
Thanks!
@@ -82,6 +82,7 @@ public void testContract() { | |||
// this test puts them at all possible locations | |||
CtType<?> toTest = typeToTest.getMetamodelInterface(); | |||
Factory factory = toTest.getFactory(); | |||
factory.getEnvironment().setIgnoreSyntaxErrors(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remember correctly, this tests tries to add some not valid children to record components. With this flag, we ignore JLSViolation
, which is thrown by CtRecord
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, it would be better to handle as a special case below (see list of special cases as of line 90). WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then the list of special cases would grow, if we add more JLS checks. A lot of tests in spoon suffer from complex lists of edge cases, we ignore. Maybe we should shrink these lists instead of adding more to improve test readability?
It's a tradeoff. By ignoring violations for all, we may silence some failures.
|
Let's move on. I'll proceed with merge. |
Many many thanks @MartinWitt |
fixes #4132
Records
This PR adds Records and Record Components to the spoon metamodel.
Records are a mix between simple tuples types and the black magic of compiler writers in 2021.
In its simplest form, a record is a tuple with fields (record components), accessor methods and
equals
/hashcode
implementation.Can be seen as
TLDR Records
Important record features:
Interesting(broken) spec features
After showing how simple records could be, let's show how are some details of the spec implemented
In the following, I try to provide the spec sentence and how it is implemented:
Abstract Modifier
Simple Check in set/add modifiers, throwing a SpoonException if the rule is violated.
Implicit final
Implemented in
DefaultCoreFactory#createRecord()
Implicit static
Implemented in
CtRecord#setParent
Superclass
Implemented in
CtRecord#setParent
Record Components
Implicit field/accessor
Implemented in
CtRecord#addRecordComponent
. RecordComponents have atoField
andtoMethod
, converting them to the corresponding accessor and field. This unfolding/folding is done for every remove and add call inCtRecord
.Clashing methods
Implemented in
CtRecordComponent#setSimpleName
TypeMembers
Implemented in
CtRecord#addTypeMemberAt
RecordMembers
Field Modifiers
Implemented in
CtRecordComponent#toField
Annotations for fields and methods
Here we had to be creative. If the annotation is known for spoon and has the matching element type, it is added. If either the annotation is not known or the element type is not matching, the annotation is omitted. The first part means that this is only a conservative approximation, the result could be wrong but the result AST is correct java code.
Record Constructors
Records can have three different constructors, either an implicit, the "normal" or a compact constructor.
The first two are well known and have to initialize all record component fields. The third is new.
Compact Constructor:
Normal Constructor
Compact constructors have 2 important differences from normal constructors:
( )
bracketsOpen Problems
toString
/equals
/hashcode
methods, because the methods are generated at runtime by the jvm. This could be added, but the value of an implicit method that could be close or totally off to the spec behaviour seems low.ReplaceParametrizedTest.testContract
test tries to add an invalid element to records. As in only a few places consistency checks are implemented, this was never an issue. A solution could be creating a new exception for JLS invalid operations subtypingSpoonException
and handling the new exception differently in the test case.ElementPrinterHelper#writeElementList
only skips implicit elements that are constructors.This leads to the caller removing implicit fields instead of the print method itself. As the printer is kinda complex I'm unsure if the methods could be changed to ignore implicit elements, but this would improve the code in printer
CtType
uses the private field typeMembers directly and does not call the getter. Using the getter strictly could improve the code inCtRecord
. But I wanted to keep the already big PR as small as possible, so this was not changed.