Skip to content

Commit

Permalink
Fixed stack overflow when requesting completion in an object literal (#…
Browse files Browse the repository at this point in the history
…15579)

When the user has an object literal value set on a module parameter of
string literal union type, we were recursing infinitely which caused the
stack overflow. We were not correctly collecting the properties from
members of the collapsed union type.

This fixes #15569.
###### Microsoft Reviewers: [Open in
CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/15579)
  • Loading branch information
majastrz authored Nov 14, 2024
1 parent b065093 commit 9e3757b
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 1 deletion.
28 changes: 28 additions & 0 deletions src/Bicep.LangServer.IntegrationTests/CompletionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
15 changes: 14 additions & 1 deletion src/Bicep.LangServer/Completions/BicepCompletionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1279,11 +1279,24 @@ private static IEnumerable<TypeProperty> 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<TypeProperty> 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),
Expand Down

0 comments on commit 9e3757b

Please sign in to comment.