config icon indicating copy to clipboard operation
config copied to clipboard

Some substitutions are resolved too early

Open varuncnaik opened this issue 6 years ago • 3 comments
trafficstars

I have created a minimal Scala 2.12 project with no dependencies and one test case that simply resolves the config files and prints the result:

class ConfigSpec extends FlatSpec with Matchers {
  "The config" should "resolve" in {
    val conf: Config = ConfigFactory.load()
    println(conf.root().render(
      ConfigRenderOptions.concise().setFormatted(true)
    ))
  }
}

I found two (probably related) errors in which a substitution gets resolved too early. In both cases, I have an application.conf, but no reference.conf.

  1. With the following application.conf:
foo = ""
foo {
  bar = ${override-me}
}
foo {
  bar = "bar"
}

I get the following exception:

com.typesafe.config.ConfigException$UnresolvedSubstitution: application.conf @ file:<redacted>/target/scala-2.12/classes/application.conf: 3: Could not resolve substitution to a value: ${override-me}

I get the same exception if I change the first line to foo = null, foo = 0, etc. Notably, foo = {} works just fine. I thought of a "fix": if I add the following object after the first line, then the config resolves correctly:

foo {
  bar = ""
}
  1. With the following application.conf, I get the same exception as above (with a different line number, of course):
empty = {}
foo = ${empty}
foo {
  bar = ${override-me}
}
foo {
  bar = "bar"
}

The "fix" from the first case doesn't seem to work here.

varuncnaik avatar Nov 26 '18 23:11 varuncnaik

At first glance, I agree, this should not be trying to resolve (since it's overridden and there was nothing forcing it to resolve). The way I'd approach this bug would probably be to add a unit test similar to these: https://github.com/lightbend/config/blob/1fc2aff45226dfc863ab8a0e54c0ae946b2b30c8/config/src/test/scala/com/typesafe/config/impl/ConfigSubstitutionTest.scala#L320-L559 and then debug it.

havocp avatar Nov 27 '18 13:11 havocp

I ran into this same issue so I went and wrote the test to debug it and the issue stems from the type disparity - foo is initially a string so when the parser encounters an unresolved object next, .withFallback goes to a mergeWithNonObject which creates a ConfigDelayedMerge which then in turn ends up trying to evaluate all three assignments to foo.

While finding the cause was relatively trivial, the fix seems far from simple - my initial impulse was to change the implementation of com.typesafe.config.impl.AbstractConfigObject#withFallback to ignore non-object value types (what does it even mean to merge an object with a list or a string?) but I didn't know the possible impacts of that decision so I thought I'd ask here first.

FingolfinTEK avatar Jul 15 '20 11:07 FingolfinTEK

Okay so applying my changes makes the following test fail com.typesafe.config.impl.ConfigTest#mergeObjectThenSubstitutionThenObject, making the merge non-associative in case of mismatching types

FingolfinTEK avatar Jul 15 '20 13:07 FingolfinTEK