Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,12 @@ ResolveResult<? extends AbstractConfigValue> resolveSubstitutions(ResolveContext
}
}

// Right now there's no reason to pushParent here because the
// content of ConfigConcatenation should not need to replaceChild,
// but if it did we'd have to do this.
ResolveSource sourceWithParent = source; // .pushParent(this);
// Pieces can contain containers with unresolved substitutions (e.g. a
// delayed merge inside a list), and resolving those replaces them via
// ResolveSource.replaceWithinCurrentParent(); for the replacement to
// walk back up to the root, this concatenation must be on the parent
// chain.
ResolveSource sourceWithParent = source.pushParent(this);
ResolveContext newContext = context;

List<AbstractConfigValue> resolved = new ArrayList<AbstractConfigValue>(pieces.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -656,4 +656,49 @@ class ConcatenationTest extends TestUtils {
parseConfig("""foo = [1], bar = [2], x = ${foo}" "${bar}""").resolve()
}
}

// https://github.com/lightbend/config/issues/751
// A delayed merge (a duplicate key whose later value is a substitution)
// inside a list that is a piece of a value concatenation used to throw
// BugOrBroken "SimpleConfigObject.replaceChild did not find ...": the
// concatenation was not on the substitution source's parent chain, so the
// resolver walked past it and could not find the list in the parent.
@Test
def delayedMergeInConcatenatedListPiece(): Unit = {
val conf = parseConfig("""
|tail = [ 2 ]
|front = [
| {
| foo = 1
| foo = ${?bar}
| }
|] ${tail}
|""".stripMargin).resolve()

val front = conf.getList("front")
assertEquals(2, front.size())
assertEquals(1, front.get(0).asInstanceOf[AbstractConfigObject].toConfig.getInt("foo"))
assertEquals(2, front.get(1).unwrapped())
}

// Same shape as delayedMergeInConcatenatedListPiece, but the substitution
// is strict and defined, so it takes part in the delayed merge.
@Test
def definedSubstitutionInConcatenatedListPiece(): Unit = {
val conf = parseConfig("""
|bar = 7
|tail = [ 2 ]
|front = [
| {
| foo = 1
| foo = ${bar}
| }
|] ${tail}
|""".stripMargin).resolve()

val front = conf.getList("front")
assertEquals(2, front.size())
assertEquals(7, front.get(0).asInstanceOf[AbstractConfigObject].toConfig.getInt("foo"))
assertEquals(2, front.get(1).unwrapped())
}
}
Loading