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

fix: remove unneeded null conditional initializer #705

Closed
wants to merge 2 commits into from

Conversation

TimothyMakkison
Copy link
Collaborator

@TimothyMakkison TimothyMakkison commented Aug 30, 2023

Description

Add check to only insert a null initializer if the root value is uninitialized or initialized by a nullable type.

tbh I feel like mapperly shouldn't insert a null initializer if the root is mapped. Having a nullable path seems like an error and this obscures it.

Partially fixes #640

Checklist

  • The existing code style is followed
  • The commit message follows our guidelines
  • Performed a self-review of my code
  • Hard-to-understand areas of my code are commented
  • The documentation is updated (as applicable)
  • Unit tests are added/updated

@codecov
Copy link

codecov bot commented Aug 30, 2023

Codecov Report

Merging #705 (8b522bf) into main (5bd3fdd) will decrease coverage by 0.60%.
The diff coverage is 94.73%.

❗ Current head 8b522bf differs from pull request most recent head 7fd4711. Consider uploading reports for the commit 7fd4711 to get more accurate results

@@            Coverage Diff             @@
##             main     #705      +/-   ##
==========================================
- Coverage   91.45%   90.86%   -0.60%     
==========================================
  Files         214      180      -34     
  Lines        7149     6325     -824     
  Branches      867      809      -58     
==========================================
- Hits         6538     5747     -791     
+ Misses        406      389      -17     
+ Partials      205      189      -16     
Files Coverage Δ
...s/BuilderContext/MembersContainerBuilderContext.cs 100.00% <100.00%> (ø)
...MemberMappings/MemberAssignmentMappingContainer.cs 96.15% <100.00%> (+0.15%) ⬆️
...scriptors/Mappings/MemberMappings/MemberMapping.cs 100.00% <100.00%> (ø)
.../Descriptors/Mappings/ObjectMemberMethodMapping.cs 90.90% <100.00%> (+0.90%) ⬆️
...Mappings/MemberMappings/MemberAssignmentMapping.cs 60.00% <83.33%> (+2.42%) ⬆️

... and 107 files with indirect coverage changes

📣 Codecov offers a browser extension for seamless coverage viewing on GitHub. Try it in Chrome or Firefox today!

@latonz latonz self-requested a review September 1, 2023 10:15
Copy link
Contributor

@latonz latonz left a comment

Choose a reason for hiding this comment

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

Thank you for this PR 😊

@@ -42,6 +42,17 @@ private void AddNullMemberInitializers(IMemberAssignmentMappingContainer contain
if (!nullablePath.Member.CanSet)
continue;

// if member is initialised with a non null value then skip
if (
Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't it be easier to keep a set of mapped source members (in SetSourceMemberMapped) and perform a simple lookup here? This way we wouldn't need to make the information public on the mapping.

Copy link
Collaborator Author

@TimothyMakkison TimothyMakkison Sep 1, 2023

Choose a reason for hiding this comment

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

Wouldn't it be easier to keep a set of mapped source members (in SetSourceMemberMapped) and perform a simple lookup here?

Tried something similar but realised it would cause errors. If the source value is transformed we'd lose the type info ie target.Value = UserDefinedDoggyMapper(source.Value), here source.Value may not be nullable but the user defined mapper might return a null value

Yeah, I'm not a fan of exposing the properties either.

Copy link
Contributor

Choose a reason for hiding this comment

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

But even in this case, we shouldn't initialize the property afterwards, should we? Wouldn't this be strange? It probably is the case today, but I don't think this makes a lot of sense.

Copy link
Collaborator Author

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 you mean, could you rephrase this please?

FYI this PR doesn't change the order of member instantiation. It is still possible to null initialise before a member is set. All this PR does is omit a null initializer if the source value is not nullable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for my bad english... I thought Mapperly should not generate a ??= new(); if previously another mapping assigned a value to this property. However, on a second thought I think we should keep the current behaviour. But isn't the described approach possible by simply adding a parameter to a Set...MemberMapped method, whether the member is nullable or not?

Copy link
Collaborator Author

@TimothyMakkison TimothyMakkison Sep 28, 2023

Choose a reason for hiding this comment

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

Have you pushed it in a branch so I can quickly look into it? 😊 Thank you!

This branch should have the changes as commit 99afa22. Do you want me to create a separate branch?

Note that I didn't remove my original modifications.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the commit. I tried a little around on the provided commit and came up with the following solution (available here):

  • I exposed a MemberTypeMapping from the IMemberAssignmentMapping
  • SetTargetNonNullInitializedIfNonNullable added to MembersContainerBuilderContext to keep track of the non-nullable initialized target paths

With this the correct nullable initializers were applied. However, under some conditions the initializers were emitted after a nested mapping. I fixed this with:

  • Introducing IMemberAssignmentMappingOrContainer to have a common interface for IMemberAssignmentMappingContainer and IMemberAssignmentMapping
  • Introducing an additional List<IMemberAssignmentMappingOrContainer> to keep the containers and mappings ordered
  • Adjusted the MemberAssignmentMappingContainer.Build to use the ordered list as source

I'm not super happy with this solution, but IMO it still exposes less information/complexity than TryGetMemberMapping. WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sound good 👍
Does this mean this PR is no longer needed!?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've rebased this pr onto a recent branch with the changes, it doesn't appear to have improved.

Any idea of what to do next? The original hack of exposing the mapping worked, aside from that I can't remember what else I tried 😅

Copy link
Contributor

Choose a reason for hiding this comment

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

Since I'm not really happy with the solutions discussed (and you don't seem to be either), I'm thinking about whether we should just remove the initialisation of these nullable values with the next major release/breaking change.

@latonz
Copy link
Contributor

latonz commented Sep 24, 2023

@TimothyMakkison any updates on this? Are you still working on this? 😊 Don't worry but otherwise I'd probably schedule this feature to be done by someone else...

@TimothyMakkison
Copy link
Collaborator Author

TimothyMakkison commented Sep 27, 2023

@TimothyMakkison any updates on this? Are you still working on this? 😊 Don't worry but otherwise I'd probably schedule this feature to be done by someone else...

Really sorry, should be able to fix this and the unmapped property warning PR. I'll need to install the preview before doing the unsafe accessors

Haven't investigated the issue, but it looks like my tests are failing. I'll look into this in the morning.

@TimothyMakkison TimothyMakkison marked this pull request as draft September 27, 2023 23:30
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.

AddNullMemberInitializers should check if a member is assigned before instantiating with ??= new()
2 participants