Skip to content

Commit 362326e

Browse files
authored
fix(.net): missing dynamic type checking for collection-nested unions (#3720)
The .NET runtime type checking was not checking type unions that are nested within a collection (list or map), although this is necessary since the `is <type>` guard does not allow making any assumption about the contents of the collection. This adds the missing checks, and replaces the guard clauses with a `switch` statement using pattern matching, resulting in somewhat more elegant code (also with better `null` safety). --- By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license]. [Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0
1 parent 897155e commit 362326e

File tree

13 files changed

+1764
-592
lines changed

13 files changed

+1764
-592
lines changed

packages/@jsii/dotnet-runtime-test/test/Amazon.JSII.Runtime.IntegrationTests/TypeCheckingTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,29 @@ public void AnonymousObjectIsValid()
7373
Assert.IsType<AnonymousObject>(anonymousObject);
7474
Assert.Equal("A", UseOptions.Consume(anonymousObject));
7575
}
76+
77+
[Fact(DisplayName = Prefix + nameof(NestedUnion))]
78+
public void NestedUnion()
79+
{
80+
var exception1 = Assert.Throws<System.ArgumentException>(() =>
81+
new ClassWithNestedUnion(new object[] { 1337.42 }));
82+
Assert.Equal("Expected argument unionProperty[0] to be one of: System.Collections.Generic.IDictionary<string, object>, object[]; received System.Double (Parameter 'unionProperty')", exception1.Message);
83+
84+
var exception2 = Assert.Throws<System.ArgumentException>(() =>
85+
new ClassWithNestedUnion(new object[]
86+
{ new object[] { new StructA { RequiredString = "present" }, 1337 } }));
87+
Assert.Equal("Expected argument unionProperty[0][1] to be one of: Amazon.JSII.Tests.CalculatorNamespace.IStructA, Amazon.JSII.Tests.CalculatorNamespace.IStructB; received System.Int32 (Parameter 'unionProperty')", exception2.Message);
88+
89+
var exception3 = Assert.Throws<System.ArgumentException>(() =>
90+
new ClassWithNestedUnion(new object[]
91+
{
92+
new Dictionary<string, object>
93+
{
94+
{ "good", new StructA { RequiredString = "present" } },
95+
{ "bad", "Not a StructA or StructB" }
96+
}
97+
}));
98+
Assert.Equal("Expected argument unionProperty[0][\"bad\"] to be one of: Amazon.JSII.Tests.CalculatorNamespace.IStructA, Amazon.JSII.Tests.CalculatorNamespace.IStructB; received System.String (Parameter 'unionProperty')", exception3.Message);
99+
}
76100
}
77101
}

packages/jsii-calc/lib/compliance.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3074,3 +3074,11 @@ export class ClassWithCollectionOfUnions {
30743074
export interface StructWithCollectionOfUnionts {
30753075
readonly unionProperty: Array<Record<string, StructA | StructB>>;
30763076
}
3077+
3078+
export class ClassWithNestedUnion {
3079+
public constructor(
3080+
public unionProperty: Array<
3081+
Array<StructA | StructB> | Record<string, StructA | StructB>
3082+
>,
3083+
) {}
3084+
}

packages/jsii-calc/test/assembly.jsii

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2976,6 +2976,136 @@
29762976
],
29772977
"symbolId": "lib/compliance:ClassWithMutableObjectLiteralProperty"
29782978
},
2979+
"jsii-calc.ClassWithNestedUnion": {
2980+
"assembly": "jsii-calc",
2981+
"docs": {
2982+
"stability": "stable"
2983+
},
2984+
"fqn": "jsii-calc.ClassWithNestedUnion",
2985+
"initializer": {
2986+
"docs": {
2987+
"stability": "stable"
2988+
},
2989+
"locationInModule": {
2990+
"filename": "lib/compliance.ts",
2991+
"line": 3079
2992+
},
2993+
"parameters": [
2994+
{
2995+
"name": "unionProperty",
2996+
"type": {
2997+
"collection": {
2998+
"elementtype": {
2999+
"union": {
3000+
"types": [
3001+
{
3002+
"collection": {
3003+
"elementtype": {
3004+
"union": {
3005+
"types": [
3006+
{
3007+
"fqn": "jsii-calc.StructA"
3008+
},
3009+
{
3010+
"fqn": "jsii-calc.StructB"
3011+
}
3012+
]
3013+
}
3014+
},
3015+
"kind": "map"
3016+
}
3017+
},
3018+
{
3019+
"collection": {
3020+
"elementtype": {
3021+
"union": {
3022+
"types": [
3023+
{
3024+
"fqn": "jsii-calc.StructA"
3025+
},
3026+
{
3027+
"fqn": "jsii-calc.StructB"
3028+
}
3029+
]
3030+
}
3031+
},
3032+
"kind": "array"
3033+
}
3034+
}
3035+
]
3036+
}
3037+
},
3038+
"kind": "array"
3039+
}
3040+
}
3041+
}
3042+
]
3043+
},
3044+
"kind": "class",
3045+
"locationInModule": {
3046+
"filename": "lib/compliance.ts",
3047+
"line": 3078
3048+
},
3049+
"name": "ClassWithNestedUnion",
3050+
"properties": [
3051+
{
3052+
"docs": {
3053+
"stability": "stable"
3054+
},
3055+
"locationInModule": {
3056+
"filename": "lib/compliance.ts",
3057+
"line": 3080
3058+
},
3059+
"name": "unionProperty",
3060+
"type": {
3061+
"collection": {
3062+
"elementtype": {
3063+
"union": {
3064+
"types": [
3065+
{
3066+
"collection": {
3067+
"elementtype": {
3068+
"union": {
3069+
"types": [
3070+
{
3071+
"fqn": "jsii-calc.StructA"
3072+
},
3073+
{
3074+
"fqn": "jsii-calc.StructB"
3075+
}
3076+
]
3077+
}
3078+
},
3079+
"kind": "map"
3080+
}
3081+
},
3082+
{
3083+
"collection": {
3084+
"elementtype": {
3085+
"union": {
3086+
"types": [
3087+
{
3088+
"fqn": "jsii-calc.StructA"
3089+
},
3090+
{
3091+
"fqn": "jsii-calc.StructB"
3092+
}
3093+
]
3094+
}
3095+
},
3096+
"kind": "array"
3097+
}
3098+
}
3099+
]
3100+
}
3101+
},
3102+
"kind": "array"
3103+
}
3104+
}
3105+
}
3106+
],
3107+
"symbolId": "lib/compliance:ClassWithNestedUnion"
3108+
},
29793109
"jsii-calc.ClassWithPrivateConstructorAndAutomaticProperties": {
29803110
"assembly": "jsii-calc",
29813111
"docs": {
@@ -18001,5 +18131,5 @@
1800118131
}
1800218132
},
1800318133
"version": "3.20.120",
18004-
"fingerprint": "w4jjSmxpafX7pHdksFL0F5FZxa/zM6MB8oFCANIU05k="
18134+
"fingerprint": "LBLJQQycukWu6zWQmp2/IbKS/Sfd+4e2zWrX+1KA+Aw="
1800518135
}

0 commit comments

Comments
 (0)