diff --git a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs index 442ac8a6cf0..c011ae5cbf7 100644 --- a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs @@ -5134,5 +5134,33 @@ param firstItem object output foo string[] = [for item in items: item.bar|] """); } + + [TestMethod] + // https://github.com/azure/bicep/issues/15569 + public async Task String_literal_union_with_object_value_should_not_cause_stack_overflow() + { + var serverHelper = new ServerRequestHelper(TestContext, DefaultServer); + + // single parameter of string literal union type + var moduleText = "param foo 'foo' | 'bar'"; + var moduleFile = await serverHelper.OpenFile("/mod.bicep", moduleText); + + var (text, cursor) = ParserHelper.GetFileWithSingleCursor(""" +targetScope = 'resourceGroup' + +module mod 'mod.bicep' = { + name: '' + params: { + foo: { + | + } + } +} +"""); + var mainFile = await serverHelper.OpenFile("/main.bicep", text); + + var completions = await mainFile.RequestCompletion(cursor); + completions.Should().BeEmpty(); + } } } diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index c2811147997..1e49bbd7995 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -1279,11 +1279,24 @@ private static IEnumerable GetProperties(TypeSymbol? type) TestType testType => GetProperties(testType.Body.Type), ObjectType objectType => objectType.Properties.Values, DiscriminatedObjectType discriminated => discriminated.DiscriminatorProperty.AsEnumerable(), - UnionType unionType => GetProperties(TypeHelper.TryCollapseTypes(unionType.Members)), + UnionType unionType => GetPropertiesFromUnionType(unionType), _ => [], }).Where(p => !p.Flags.HasFlag(TypePropertyFlags.FallbackProperty)); } + private static IEnumerable GetPropertiesFromUnionType(UnionType unionType) + { + var potentiallyCollapsedType = TypeHelper.TryCollapseTypes(unionType.Members); + if(potentiallyCollapsedType is UnionType) + { + // type collapsed into a new or same union type (may have collapsed into itself) + // get properties from each union members + return unionType.Members.SelectMany(member => GetProperties(member.Type)); + } + + return GetProperties(potentiallyCollapsedType); + } + private static TypeSymbol? GetAdditionalPropertiesType(TypeSymbol? type) => type switch { ResourceType resourceType => GetAdditionalPropertiesType(resourceType.Body.Type),