Fix resolveWith() for overloaded keys with delayed merge - #856
Conversation
|
Hi @arimu1, Thank you for your contribution! We really value the time you've taken to put this together. Before we proceed with reviewing this pull request, please sign the Akka Contributors License Agreement: |
When resolveWith() uses an external substitution root, parent-chain tracking was skipped and ConfigDelayedMerge could not replace itself during inner substitution resolution, throwing BugOrBroken. Fixes lightbend#855
1c11f1a to
dadf7f2
Compare
johanandren
left a comment
There was a problem hiding this comment.
Thanks for tracking this down, the diagnosis is right, and the behaviour the PR produces is right too.
The main feedback is to move the fix down to the branch that actually throws, that will be twice as fast and it leaves pushParent's documented invariant and its ***** BUG ***** trace in place.
Also, we'd need a signed CLA to be able to merge this.
|
|
||
| // https://github.com/lightbend/config/issues/855 | ||
| @Test | ||
| def resolveWithDelayedMergeOverloadedValue(): Unit = { |
There was a problem hiding this comment.
I think this would be a more descriptive/correct test name
| def resolveWithDelayedMergeOverloadedValue(): Unit = { | |
| def resolveWithDuplicateKeyAndSubstitution(): Unit = { |
There was a problem hiding this comment.
Put the fix here instead, splitting the combined old == root && replacement instanceof Container condition is what makes the two cases distinguishable: old == root keeps main's behaviour exactly, including the throw when the replacement isn't a Container; only "not a descendant of root at all" becomes a no-op.
} else if (old == root) {
if (replacement instanceof Container) {
return new ResolveSource(rootMustBeObj((Container) replacement));
} else {
throw new ConfigException.BugOrBroken("replace in parent not possible " + old + " with " + replacement + " in " + this);
}
} else {
// pathFromRoot is null so we aren't proceeding from the root (see
// pushParent); resolveWith() resolves a value that isn't a descendant
// of the substitution root, so root can't contain "old" and there's
// nothing to replace.
return this;
}The comment is doing the : replaceWithinCurrentParent exists so a delayed merge can hide itself from its own substitutions, and when the lookup root doesn't contain the merge stack there is nothing to hide.
|
Addressed the review feedback in 30c2252:
CLA still needs to be signed by the human contributor before merge. |
Keep old==root throw when replacement is not a Container; return this when old is not a descendant of the substitution root. Rename test to resolveWithDuplicateKeyAndSubstitution.
30c2252 to
167717f
Compare
Summary
ConfigException.BugOrBrokenwhen callingresolveWith()on a config that has duplicate keys where the later value is a substitution (creating aConfigDelayedMerge).resolveWith()does).${variable}still resolves from theresolveWithsource.Reproduces and fixes #855:
Test plan
resolveWithDelayedMergeOverloadedValueregression test inConfigTestsbt config/test— 553 tests passed (JDK 17)Fixes #855
Made with Cursor