Skip to content

Commit

Permalink
Merge #13630
Browse files Browse the repository at this point in the history
13630: [dotnet/program-gen] Fixes list initializer for plain lists in resource properties r=Zaid-Ajaj a=Zaid-Ajaj

# Description

Some resources, especially custom resources like those in `awsx` have properties which are _plain_ lists. This means that the generated dotnet SDK for them uses `List<T>` rather than `InputList<T>`. In case of the former, when initializing the list, we cannot use `new[]` (this works for `InputList<T>` because of an implicit conversion from arrays). Instead we have to use `new()` which initializes an instance of the `List<T>` class. 

This PR implements a fix such that the code generator knows when the current resource property is plain or not and subsequently emitting `new()` instead of `new[]`

Fixes pulumi/pulumi-dotnet#21

## Checklist

- [ ] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
  - [ ] I have formatted my code using `gofumpt`

<!--- Please provide details if the checkbox below is to be left unchecked. -->
- [x] I have added tests that prove my fix is effective or that my feature works
<!--- 
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the `changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the Pulumi Cloud,
then the service should honor older versions of the CLI where this change would not exist.
You must then bump the API version in /pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi Cloud API version
  <!-- `@Pulumi` employees: If yes, you must submit corresponding changes in the service repo. -->


Co-authored-by: Zaid Ajaj <zaid.naom@gmail.com>
  • Loading branch information
bors[bot] and Zaid-Ajaj committed Aug 7, 2023
2 parents 474f30a + d66a037 commit 9cf9a0b
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 5 deletions.
@@ -0,0 +1,4 @@
changes:
- type: fix
scope: programgen/dotnet
description: Fixes list initializer for plain lists in resource properties
29 changes: 29 additions & 0 deletions pkg/codegen/dotnet/gen_program.go
Expand Up @@ -73,6 +73,15 @@ type generator struct {
// Program generation options
generateOptions GenerateProgramOptions
isComponent bool
// when creating a list of items, we need to know the type of the list
// if is it a plain list, then `new()` should be used because we are creating List<T>
// however if we have InputList<T> or anything else, we use `new[]` because InputList<T> can be implicitly casted
// from an array
listInitializer string
}

func (g *generator) resetListInitializer() {
g.listInitializer = "new[]"
}

const (
Expand Down Expand Up @@ -126,6 +135,7 @@ func GenerateProgramWithOptions(
functionArgs: functionArgs,
functionInvokes: map[string]*schema.Function{},
generateOptions: options,
listInitializer: "new[]",
}

g.Formatter = format.NewFormatter(g)
Expand Down Expand Up @@ -164,6 +174,7 @@ func GenerateProgramWithOptions(
functionInvokes: map[string]*schema.Function{},
generateOptions: options,
isComponent: true,
listInitializer: "new[]",
}

componentGenerator.Formatter = format.NewFormatter(componentGenerator)
Expand Down Expand Up @@ -1205,6 +1216,19 @@ func AnnotateComponentInputs(component *pcl.Component) {
}
}

func isPlainResourceProperty(r *pcl.Resource, name string) bool {
if r.Schema == nil {
return false
}

for _, property := range r.Schema.InputProperties {
if property.Name == name {
return property.Plain
}
}
return false
}

// genResource handles the generation of instantiations of non-builtin resources.
func (g *generator) genResource(w io.Writer, r *pcl.Resource) {
qualifiedMemberName := g.resourceTypeName(r)
Expand Down Expand Up @@ -1247,7 +1271,12 @@ func (g *generator) genResource(w io.Writer, r *pcl.Resource) {
g.Indented(func() {
for _, attr := range r.Inputs {
g.Fgenf(w, "%s%s =", g.Indent, propertyName(attr.Name))
if isPlainResourceProperty(r, attr.Name) {
g.listInitializer = "new()"
}

g.Fgenf(w, " %.v,\n", attr.Value)
g.resetListInitializer()
}
})
g.Fgenf(w, "%s}%s)", g.Indent, g.genResourceOptions(r.Options, "CustomResourceOptions"))
Expand Down
10 changes: 5 additions & 5 deletions pkg/codegen/dotnet/gen_program_expressions.go
Expand Up @@ -1032,14 +1032,14 @@ func (g *generator) isListOfDifferentTypes(expr *model.TupleConsExpression) bool
func (g *generator) GenTupleConsExpression(w io.Writer, expr *model.TupleConsExpression) {
switch len(expr.Expressions) {
case 0:
g.Fgen(w, "new[] {}")
g.Fgenf(w, "%s {}", g.listInitializer)
default:
if !g.isListOfDifferentTypes(expr) {
// only generate this when we don't have a list of union types
// list of a union is mapped to InputList<object>
// only generate a list initializer when we don't have a list of union types
// because list of a union is mapped to InputList<object>
// which means new[] will not work because type-inference won't
// know the type of the array before hand
g.Fgen(w, "new[]")
// know the type of the array beforehand
g.Fgenf(w, "%s", g.listInitializer)
}

g.Fgenf(w, "\n%s{", g.Indent)
Expand Down
5 changes: 5 additions & 0 deletions pkg/codegen/testing/test/program_driver.go
Expand Up @@ -361,6 +361,11 @@ var PulumiPulumiProgramTests = []ProgramTest{
Description: "Tests that interpolated string keys are supported in maps. ",
Skip: allProgLanguages.Except("nodejs").Except("python"),
},
{
Directory: "csharp-plain-lists",
Description: "Tests that plain lists are supported in C#",
Skip: allProgLanguages.Except("dotnet"),
},
{
Directory: "csharp-typed-for-expressions",
Description: "Testing for expressions with typed target expressions in csharp",
Expand Down
@@ -0,0 +1,6 @@
resource vpc "awsx:ec2:Vpc" {
subnetSpecs = [
{ type = "Public", cidrMask = 22 },
{ type = "Private", cidrMask = 20 }
]
}
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Awsx = Pulumi.Awsx;

return await Deployment.RunAsync(() =>
{
var vpc = new Awsx.Ec2.Vpc("vpc", new()
{
SubnetSpecs = new()
{
new Awsx.Ec2.Inputs.SubnetSpecArgs
{
Type = Awsx.Ec2.SubnetType.Public,
CidrMask = 22,
},
new Awsx.Ec2.Inputs.SubnetSpecArgs
{
Type = Awsx.Ec2.SubnetType.Private,
CidrMask = 20,
},
},
});
});

0 comments on commit 9cf9a0b

Please sign in to comment.