lightbend / config

configuration library for JVM languages using HOCON files
https://lightbend.github.io/config/
6.12k stars 968 forks source link

Rendering partially resolved appended array results in invalid configuration #800

Open tmccombs opened 7 months ago

tmccombs commented 7 months ago

Consider the following (scala) code using this library:

val c = ConfigFactory.parseString("""
  a = [${s}-1]
  a += ${s}-2
  a += ${s}-3
  a += ${s}-4
  """) 
val r = c.resolve(ConfigResolveOptions.defaults.setAllowUnresolved(true)) 

print(r.root.render(ConfigRenderOptions.concise.setFormatted(true))) 

This outputs the following invalid configuration:

{
        "a" : [
        ${s}-1
    ],
    "a" : [
        ${s}-1,
        ${s}-2
    ],
    "a" :     [
        ${s}-1
    ],
    [
        ${s}-1,
        ${s}-2
    ]
[
        ${s}-3
    ],
    "a" :     [
        ${s}-1
    ],
    [
        ${s}-1,
        ${s}-2
    ],
        [
        ${s}-1
    ],
    [
        ${s}-1,
        ${s}-2
    ]
[
        ${s}-3
    ]
[
        ${s}-4
    ]

}

I then can't use this output to later as input.

The output I would expect would be:


a = [${s}-1, ${s}-2, ${s}-3, ${s}-4]

Furthermore, if I try to combine r with another configuration that sets s to a string, either with r as the fallback, or the other config as the fallback, then try to call resolve I get:

com.typesafe.config.ConfigException$BugOrBroken: replace in parent not possible ConfigDelayedMerge([${s}-1],[${s}-1,${s}-2],[${s}-1],[${s}-1,${s}-2][${s}-3]) with ConfigDelayedMerge([${s}-1],[${s}-1,${s}-2]) in ResolveSource(root=SimpleConfigObject({"a":[${s}-1],"a":[${s}-1,${s}-2],"a":[${s}-1],[${s}-1,${s}-2][${s}-3],"s":"abc"}), pathFromRoot=null)
  com.typesafe.config.impl.ResolveSource.replaceWithinCurrentParent(ResolveSource.java:245)
  com.typesafe.config.impl.ConfigDelayedMerge.resolveSubstitutions(ConfigDelayedMerge.java:110)
  com.typesafe.config.impl.ConfigDelayedMerge.resolveSubstitutions(ConfigDelayedMerge.java:59)
  com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:183)
  com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146)
  com.typesafe.config.impl.ConfigConcatenation.resolveSubstitutions(ConfigConcatenation.java:205)
  com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:183)
  com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146)
  com.typesafe.config.impl.ConfigDelayedMerge.resolveSubstitutions(ConfigDelayedMerge.java:132)
  com.typesafe.config.impl.ConfigDelayedMerge.resolveSubstitutions(ConfigDelayedMerge.java:59)
  com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:183)
  com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146)
  com.typesafe.config.impl.SimpleConfigObject$ResolveModifier.modifyChildMayThrow(SimpleConfigObject.java:380)
  com.typesafe.config.impl.SimpleConfigObject.modifyMayThrow(SimpleConfigObject.java:313)
  com.typesafe.config.impl.SimpleConfigObject.resolveSubstitutions(SimpleConfigObject.java:399)
  com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:183)
  com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146)
  com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:235)
  com.typesafe.config.impl.SimpleConfig.resolveWith(SimpleConfig.java:79)
  com.typesafe.config.impl.SimpleConfig.resolve(SimpleConfig.java:69)
  com.typesafe.config.impl.SimpleConfig.resolve(SimpleConfig.java:64)
  com.typesafe.config.impl.SimpleConfig.resolve(SimpleConfig.java:42)
  ammonite.$sess.cmd34$.<clinit>(cmd34.sc:1)

Also, if you have a large number of += operations on the same array, rendering can easily result in an out of memory exception, since the length of the output is O(n^2).

tmccombs commented 7 months ago

Would a PR to fix this be welcome? Or would that be "extending functionality" and thus be rejected?