akkadotnet / HOCON

C# implementation of Lightbend's HOCON (Human-Optimized Object Configuration Notation)
Apache License 2.0
137 stars 39 forks source link

Unexpected behaviour during substitution chain #390

Open g-jozsef opened 1 year ago

g-jozsef commented 1 year ago

Version Information Version of Akka.NET? N/A (using it standalone) Which Akka.NET Modules? N/A

Describe the bug When having a transitive substitution with no changed values (observed behavior) HOCON throws a cyclic reference exception.

To Reproduce using 2.0.4 from nuget:

using System;
using Hocon;

public class Program
{
    public static void Main()
    {
        var c = @"
        default {
            some-variable = ""some-value""
        }

        data = ${default} {
            some-variable = ""some-value2""
        }

        item = ${data} {
        }";
        Console.WriteLine("loading... " + c);
        Console.WriteLine("loaded: \n" + HoconConfigurationFactory.ParseString(c).PrettyPrint(2));
    }
}

correctly prints

loaded: 
{
  default : {
    some-variable : "some-value"
  },
  data : {
    some-variable : "some-value2"
  },
  item : {
    some-variable : "some-value2"
  }
}

but

using System;
using Hocon;

public class Program
{
    public static void Main()
    {
        var c = @"
    default {
            some-variable = ""some-value""
        }

        data = ${default} {
            some-variable = ""some-value""
        }

        item = ${data} {
        }";
        Console.WriteLine("loading... " + c);
        Console.WriteLine("loaded: \n" + HoconConfigurationFactory.ParseString(c).PrettyPrint(2));
    }
}

throws

Unhandled exception. Hocon.HoconParserException: Invalid substitution declaration. A cyclic substitution loop is detected in the Hocon file.. At path 'data', line 9, position 20.
 ---> Hocon.HoconException: A cyclic substitution loop is detected in the Hocon file.
   at Hocon.HoconParser.ResolveSubstitution(HoconSubstitution sub)
   at Hocon.HoconParser.ResolveSubstitutions()
   --- End of inner exception stack trace ---
   at Hocon.HoconParser.ResolveSubstitutions()
   at Hocon.HoconParser.ParseText(String text, Boolean resolveSubstitutions, HoconIncludeCallbackAsync includeCallback)
   at Hocon.HoconParser.Parse(String text, HoconIncludeCallbackAsync includeCallback)
   at Hocon.HoconConfigurationFactory.ParseString(String hocon, HoconIncludeCallbackAsync includeCallback)
   at Hocon.HoconConfigurationFactory.ParseString(String hocon)
   at Program.Main()

Please NOTE: Only difference between the two is this line:

        data = ${default} {
+            some-variable = ""some-value""
-            some-variable = ""some-value2""
        }

Expected behavior I expected item's some-variable to inherit "some-value". OR both cases should throw if transitive substitution is not allowed.

  item : {
    some-variable : "some-value"
  }

Actual behavior A cyclic substitution loop is detected when there isn't one.

Screenshots N/A

Environment Issue detected in my large project on Windows, using dotnet 7.0.102. I wrote this example on https://dotnetfiddle.net/ using .NET 7 but the issue reproduces in other dotnet versions as well (and of course in a larger scale in my project... it took a while to pinpoint to this minimal reproducible version :) )

Additional context N/A