diff --git a/packages/jsii-dotnet-analyzers/.gitignore b/packages/jsii-dotnet-analyzers/.gitignore
new file mode 100644
index 0000000000..65d6c8ce34
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/.gitignore
@@ -0,0 +1,17 @@
+# generated by generate.sh
+src/Amazon.JSII.Generator/JsiiVersion.cs
+src/Directory.Build.props
+src/NuGet.Metadata.props
+
+*.js
+*.d.ts
+node_modules/
+.nyc_output/
+coverage/
+
+
+*.nupkg
+bin/
+cli/
+obj/
+*.DotSettings.user
diff --git a/packages/jsii-dotnet-analyzers/Directory.Build.props.t.js b/packages/jsii-dotnet-analyzers/Directory.Build.props.t.js
new file mode 100644
index 0000000000..77bca5d03c
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/Directory.Build.props.t.js
@@ -0,0 +1,9 @@
+const version = require('./package.json').version.replace(/\+.+$/, ''); // omit "+build" suffix
+
+process.stdout.write(`
+
+ ${version}
+ netstandard2.0
+
+
+`);
diff --git a/packages/jsii-dotnet-analyzers/LICENSE b/packages/jsii-dotnet-analyzers/LICENSE
new file mode 100644
index 0000000000..add725cd48
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/packages/jsii-dotnet-analyzers/NuGet.Metadata.props.t.js b/packages/jsii-dotnet-analyzers/NuGet.Metadata.props.t.js
new file mode 100644
index 0000000000..e0c50b6ad4
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/NuGet.Metadata.props.t.js
@@ -0,0 +1,19 @@
+const package = require('./package.json');
+
+process.stdout.write(`
+
+ True
+ True
+ True
+ ..\\..\\bin\\$(Configuration)\\NuGet\\
+ $(JsiiVersion)
+ ${package.description}
+ ${package.homepage}
+ https://spdx.org/licenses/${package.license}.html
+ ${package.author.name}
+ ${package.author.name}
+ key.snk
+ True
+
+
+`);
diff --git a/packages/jsii-dotnet-analyzers/build.sh b/packages/jsii-dotnet-analyzers/build.sh
new file mode 100644
index 0000000000..93d1136aa6
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/build.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -euo pipefail
+
+dotnet build --force -c Release ./src/Amazon.JSII.Analyzers
+
+cp -f ./bin/Release/NuGet/*.nupkg .
diff --git a/packages/jsii-dotnet-analyzers/generate.sh b/packages/jsii-dotnet-analyzers/generate.sh
new file mode 100644
index 0000000000..5a5764b0d9
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/generate.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -euo pipefail
+src="./src"
+
+# Generate metadata files based on package.json.
+/usr/bin/env node ./Directory.Build.props.t.js > ${src}/Directory.Build.props
+/usr/bin/env node ./NuGet.Metadata.props.t.js > ${src}/NuGet.Metadata.props
diff --git a/packages/jsii-dotnet-analyzers/lib/index.ts b/packages/jsii-dotnet-analyzers/lib/index.ts
new file mode 100644
index 0000000000..838fecdac4
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/lib/index.ts
@@ -0,0 +1,3 @@
+import path = require('path');
+
+export const repository = path.resolve(__dirname, path.join('..', 'bin', 'Release', 'NuGet'));
diff --git a/packages/jsii-dotnet-analyzers/package-lock.json b/packages/jsii-dotnet-analyzers/package-lock.json
new file mode 100644
index 0000000000..6b6d4d37f6
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/package-lock.json
@@ -0,0 +1,15 @@
+{
+ "name": "jsii-dotnet-analyzers",
+ "version": "0.15.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "jsii-build-tools": {
+ "version": "file:../jsii-build-tools",
+ "dev": true
+ },
+ "jsii-dotnet-runtime": {
+ "version": "file:../jsii-dotnet-runtime"
+ }
+ }
+}
diff --git a/packages/jsii-dotnet-analyzers/package.json b/packages/jsii-dotnet-analyzers/package.json
new file mode 100644
index 0000000000..df169759bc
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "jsii-dotnet-analyzers",
+ "version": "0.15.0",
+ "description": ".NET Roslyn Analyzers for Jsii",
+ "main": "lib/index.js",
+ "private": true,
+ "types": "lib/index.d.ts",
+ "scripts": {
+ "gen": "/bin/bash ./generate.sh",
+ "build": "npm run gen && tsc --build && /bin/bash ./build.sh",
+ "test": "/bin/bash ./test.sh",
+ "package": "package-dotnet"
+ },
+ "devDependencies": {
+ "jsii-build-tools": "file:../jsii-build-tools"
+ },
+ "dependencies": {
+ "jsii-dotnet-runtime": "file:../jsii-dotnet-runtime"
+ },
+ "author": {
+ "name": "Amazon Web Services",
+ "url": "https://aws.amazon.com",
+ "email": "aws-jsii@amazon.com"
+ },
+ "license": "Apache-2.0",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/aws/jsii.git",
+ "directory": "packages/jsii-dotnet-analyzers"
+ },
+ "homepage": "https://github.com/aws/jsii"
+}
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Amazon.JSII.Analyzers.UnitTests.csproj b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Amazon.JSII.Analyzers.UnitTests.csproj
new file mode 100755
index 0000000000..7a61bc6c1a
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Amazon.JSII.Analyzers.UnitTests.csproj
@@ -0,0 +1,25 @@
+
+
+
+ netcoreapp2.0
+ false
+ Amazon.JSII.Analyzers.UnitTests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Helpers/DiagnosticResult.cs b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Helpers/DiagnosticResult.cs
new file mode 100755
index 0000000000..dde80c43f9
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Helpers/DiagnosticResult.cs
@@ -0,0 +1,87 @@
+using Microsoft.CodeAnalysis;
+using System;
+
+namespace TestHelper
+{
+ ///
+ /// Location where the diagnostic appears, as determined by path, line number, and column number.
+ ///
+ public struct DiagnosticResultLocation
+ {
+ public DiagnosticResultLocation(string path, int line, int column)
+ {
+ if (line < -1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(line), "line must be >= -1");
+ }
+
+ if (column < -1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(column), "column must be >= -1");
+ }
+
+ this.Path = path;
+ this.Line = line;
+ this.Column = column;
+ }
+
+ public string Path { get; }
+ public int Line { get; }
+ public int Column { get; }
+ }
+
+ ///
+ /// Struct that stores information about a Diagnostic appearing in a source
+ ///
+ public struct DiagnosticResult
+ {
+ private DiagnosticResultLocation[] locations;
+
+ public DiagnosticResultLocation[] Locations
+ {
+ get
+ {
+ if (this.locations == null)
+ {
+ this.locations = new DiagnosticResultLocation[] { };
+ }
+ return this.locations;
+ }
+
+ set
+ {
+ this.locations = value;
+ }
+ }
+
+ public DiagnosticSeverity Severity { get; set; }
+
+ public string Id { get; set; }
+
+ public string Message { get; set; }
+
+ public string Path
+ {
+ get
+ {
+ return this.Locations.Length > 0 ? this.Locations[0].Path : "";
+ }
+ }
+
+ public int Line
+ {
+ get
+ {
+ return this.Locations.Length > 0 ? this.Locations[0].Line : -1;
+ }
+ }
+
+ public int Column
+ {
+ get
+ {
+ return this.Locations.Length > 0 ? this.Locations[0].Column : -1;
+ }
+ }
+ }
+}
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Helpers/DiagnosticVerifier.Helper.cs b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Helpers/DiagnosticVerifier.Helper.cs
new file mode 100755
index 0000000000..de72e54f8d
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Helpers/DiagnosticVerifier.Helper.cs
@@ -0,0 +1,170 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Text;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace TestHelper
+{
+ ///
+ /// Class for turning strings into documents and getting the diagnostics on them
+ /// All methods are static
+ ///
+ public abstract partial class DiagnosticVerifier
+ {
+ private static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
+ private static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location);
+ private static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location);
+ private static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location);
+
+ internal static string DefaultFilePathPrefix = "Test";
+ internal static string CSharpDefaultFileExt = "cs";
+ internal static string VisualBasicDefaultExt = "vb";
+ internal static string TestProjectName = "TestProject";
+
+ #region Get Diagnostics
+
+ ///
+ /// Given classes in the form of strings, their language, and an IDiagnosticAnalyzer to apply to it, return the diagnostics found in the string after converting it to a document.
+ ///
+ /// Classes in the form of strings
+ /// The language the source classes are in
+ /// The analyzer to be run on the sources
+ /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location
+ private static Diagnostic[] GetSortedDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer)
+ {
+ return GetSortedDiagnosticsFromDocuments(analyzer, GetDocuments(sources, language));
+ }
+
+ ///
+ /// Given an analyzer and a document to apply it to, run the analyzer and gather an array of diagnostics found in it.
+ /// The returned diagnostics are then ordered by location in the source document.
+ ///
+ /// The analyzer to run on the documents
+ /// The Documents that the analyzer will be run on
+ /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location
+ protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyzer analyzer, Document[] documents)
+ {
+ var projects = new HashSet();
+ foreach (var document in documents)
+ {
+ projects.Add(document.Project);
+ }
+
+ var diagnostics = new List();
+ foreach (var project in projects)
+ {
+ var compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer));
+ var diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result;
+ foreach (var diag in diags)
+ {
+ if (diag.Location == Location.None || diag.Location.IsInMetadata)
+ {
+ diagnostics.Add(diag);
+ }
+ else
+ {
+ for (int i = 0; i < documents.Length; i++)
+ {
+ var document = documents[i];
+ var tree = document.GetSyntaxTreeAsync().Result;
+ if (tree == diag.Location.SourceTree)
+ {
+ diagnostics.Add(diag);
+ }
+ }
+ }
+ }
+ }
+
+ var results = SortDiagnostics(diagnostics);
+ diagnostics.Clear();
+ return results;
+ }
+
+ ///
+ /// Sort diagnostics by location in source document
+ ///
+ /// The list of Diagnostics to be sorted
+ /// An IEnumerable containing the Diagnostics in order of Location
+ private static Diagnostic[] SortDiagnostics(IEnumerable diagnostics)
+ {
+ return diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
+ }
+
+ #endregion
+
+ #region Set up compilation and documents
+ ///
+ /// Given an array of strings as sources and a language, turn them into a project and return the documents and spans of it.
+ ///
+ /// Classes in the form of strings
+ /// The language the source code is in
+ /// A Tuple containing the Documents produced from the sources and their TextSpans if relevant
+ private static Document[] GetDocuments(string[] sources, string language)
+ {
+ if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic)
+ {
+ throw new ArgumentException("Unsupported Language");
+ }
+
+ var project = CreateProject(sources, language);
+ var documents = project.Documents.ToArray();
+
+ if (sources.Length != documents.Length)
+ {
+ throw new InvalidOperationException("Amount of sources did not match amount of Documents created");
+ }
+
+ return documents;
+ }
+
+ ///
+ /// Create a Document from a string through creating a project that contains it.
+ ///
+ /// Classes in the form of a string
+ /// The language the source code is in
+ /// A Document created from the source string
+ protected static Document CreateDocument(string source, string language = LanguageNames.CSharp)
+ {
+ return CreateProject(new[] { source }, language).Documents.First();
+ }
+
+ ///
+ /// Create a project using the inputted strings as sources.
+ ///
+ /// Classes in the form of strings
+ /// The language the source code is in
+ /// A Project created out of the Documents created from the source strings
+ private static Project CreateProject(string[] sources, string language = LanguageNames.CSharp)
+ {
+ string fileNamePrefix = DefaultFilePathPrefix;
+ string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt;
+
+ var projectId = ProjectId.CreateNewId(debugName: TestProjectName);
+
+ var solution = new AdhocWorkspace()
+ .CurrentSolution
+ .AddProject(projectId, TestProjectName, TestProjectName, language)
+ .AddMetadataReference(projectId, CorlibReference)
+ .AddMetadataReference(projectId, SystemCoreReference)
+ .AddMetadataReference(projectId, CSharpSymbolsReference)
+ .AddMetadataReference(projectId, CodeAnalysisReference);
+
+ int count = 0;
+ foreach (var source in sources)
+ {
+ var newFileName = fileNamePrefix + count + "." + fileExt;
+ var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName);
+ solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
+ count++;
+ }
+ return solution.GetProject(projectId);
+ }
+ #endregion
+ }
+}
+
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/JsiiOptionalAnalyzerTests.cs b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/JsiiOptionalAnalyzerTests.cs
new file mode 100755
index 0000000000..18a1de12bf
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/JsiiOptionalAnalyzerTests.cs
@@ -0,0 +1,173 @@
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Xunit;
+using TestHelper;
+
+namespace Amazon.JSII.Analyzers.UnitTests
+{
+ public class JsiiOptionalAnalyzerTests : DiagnosticVerifier
+ {
+ [Fact]
+ public void TestGivenSomeEmptyCodeThenRoslynDoesNotComplain()
+ {
+ var test = @"";
+
+ VerifyCSharpDiagnostic(test);
+ }
+
+ [Fact]
+ public void TestGivenSomeCodeWithAMissingRequiredPropertyThenRoslynComplains()
+ {
+ var test = @"
+ using System;
+ namespace Amazon.JSII.Analyzers.UnitTests
+ {
+ public class JsiiByValueAttribute : Attribute
+ {
+ }
+
+ public class JsiiClassAttribute : Attribute
+ {
+ }
+
+ public class JsiiOptionalAttribute : Attribute
+ {
+ }
+
+ [JsiiByValue]
+ public class SampleProps
+ {
+ [JsiiOptional]
+ public string OptionalProperty1 { get; set; }
+
+ [JsiiOptional]
+ public string OptionalProperty2 { get; set; }
+
+ public string RequiredProperty1 { get; set; }
+
+ public string RequiredProperty2 { get; set; }
+ }
+
+ [JsiiClass]
+ public class SampleClass
+ {
+ public SampleClass(SampleProps props)
+ {
+ props = null;
+ }
+ }
+
+ class Test
+ {
+ // This should fail because there is missing required properties and it is a nested instruction
+ var result1 = new SampleClass(new SampleProps());
+
+ // This should fail because RequiredProperty1 is passed as null
+ var result2 = new SampleClass(new SampleProps()
+ {
+ RequiredProperty1 = null,
+ OptionalProperty2 = ""test"",
+ RequiredProperty2 = ""test""
+ });
+
+ // This is OK, the properties might be passed later, we don't want to enforce it
+ var result3 = new SampleProps();
+
+ // This is not OK, if you start passing properties, you should pass all of the required ones
+ var result4 = new SampleProps()
+ {
+ RequiredProperty1 = null,
+ OptionalProperty2 = ""test""
+ };
+
+ // This is not OK, RequiredProperty1 is null
+ var result5 = new SampleProps()
+ {
+ RequiredProperty1 = null,
+ OptionalProperty2 = ""test"",
+ RequiredProperty2 = ""test""
+ };
+
+ // This is OK, all required properties are passed and not null.
+ var result6 = new SampleProps()
+ {
+ RequiredProperty1 = ""test"",
+ OptionalProperty2 = ""test"",
+ RequiredProperty2 = ""test""
+ };
+ }
+ }";
+ var expected = new List()
+ {
+ new DiagnosticResult()
+ {
+ Id = "JSII001",
+ Message = "The property RequiredProperty1 is required and cannot be null",
+ Severity = DiagnosticSeverity.Error,
+ Locations =
+ new[] {
+ new DiagnosticResultLocation("Test0.cs", 43, 51)
+ }
+ },
+ new DiagnosticResult()
+ {
+ Id = "JSII001",
+ Message = "The property RequiredProperty2 is required and cannot be null",
+ Severity = DiagnosticSeverity.Error,
+ Locations =
+ new[] {
+ new DiagnosticResultLocation("Test0.cs", 43, 51)
+ }
+ },
+ new DiagnosticResult()
+ {
+ Id = "JSII001",
+ Message = "The property RequiredProperty1 is required and cannot be null",
+ Severity = DiagnosticSeverity.Error,
+ Locations =
+ new[] {
+ new DiagnosticResultLocation("Test0.cs", 46, 51)
+ }
+ },
+ new DiagnosticResult()
+ {
+ Id = "JSII001",
+ Message = "The property RequiredProperty1 is required and cannot be null",
+ Severity = DiagnosticSeverity.Error,
+ Locations =
+ new[] {
+ new DiagnosticResultLocation("Test0.cs", 57, 35)
+ }
+ },
+ new DiagnosticResult()
+ {
+ Id = "JSII001",
+ Message = "The property RequiredProperty2 is required and cannot be null",
+ Severity = DiagnosticSeverity.Error,
+ Locations =
+ new[] {
+ new DiagnosticResultLocation("Test0.cs", 57, 35)
+ }
+ },
+ new DiagnosticResult()
+ {
+ Id = "JSII001",
+ Message = "The property RequiredProperty1 is required and cannot be null",
+ Severity = DiagnosticSeverity.Error,
+ Locations =
+ new[] {
+ new DiagnosticResultLocation("Test0.cs", 64, 35)
+ }
+ }
+ };
+
+ VerifyCSharpDiagnostic(test, expected.ToArray());
+ }
+
+ protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
+ {
+ return new JsiiOptionalAnalyzer();
+ }
+ }
+}
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Verifiers/DiagnosticVerifier.cs b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Verifiers/DiagnosticVerifier.cs
new file mode 100755
index 0000000000..8a66157221
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.UnitTests/Verifiers/DiagnosticVerifier.cs
@@ -0,0 +1,269 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Xunit;
+
+namespace TestHelper
+{
+ ///
+ /// Superclass of all Unit Tests for DiagnosticAnalyzers
+ ///
+ public abstract partial class DiagnosticVerifier
+ {
+ #region To be implemented by Test classes
+ ///
+ /// Get the CSharp analyzer being tested - to be implemented in non-abstract class
+ ///
+ protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
+ {
+ return null;
+ }
+
+ ///
+ /// Get the Visual Basic analyzer being tested (C#) - to be implemented in non-abstract class
+ ///
+ protected virtual DiagnosticAnalyzer GetBasicDiagnosticAnalyzer()
+ {
+ return null;
+ }
+ #endregion
+
+ #region Verifier wrappers
+
+ ///
+ /// Called to test a C# DiagnosticAnalyzer when applied on the single inputted string as a source
+ /// Note: input a DiagnosticResult for each Diagnostic expected
+ ///
+ /// A class in the form of a string to run the analyzer on
+ /// DiagnosticResults that should appear after the analyzer is run on the source
+ protected void VerifyCSharpDiagnostic(string source, params DiagnosticResult[] expected)
+ {
+ VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected);
+ }
+
+ ///
+ /// Called to test a VB DiagnosticAnalyzer when applied on the single inputted string as a source
+ /// Note: input a DiagnosticResult for each Diagnostic expected
+ ///
+ /// A class in the form of a string to run the analyzer on
+ /// DiagnosticResults that should appear after the analyzer is run on the source
+ protected void VerifyBasicDiagnostic(string source, params DiagnosticResult[] expected)
+ {
+ VerifyDiagnostics(new[] { source }, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected);
+ }
+
+ ///
+ /// Called to test a C# DiagnosticAnalyzer when applied on the inputted strings as a source
+ /// Note: input a DiagnosticResult for each Diagnostic expected
+ ///
+ /// An array of strings to create source documents from to run the analyzers on
+ /// DiagnosticResults that should appear after the analyzer is run on the sources
+ protected void VerifyCSharpDiagnostic(string[] sources, params DiagnosticResult[] expected)
+ {
+ VerifyDiagnostics(sources, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected);
+ }
+
+ ///
+ /// Called to test a VB DiagnosticAnalyzer when applied on the inputted strings as a source
+ /// Note: input a DiagnosticResult for each Diagnostic expected
+ ///
+ /// An array of strings to create source documents from to run the analyzers on
+ /// DiagnosticResults that should appear after the analyzer is run on the sources
+ protected void VerifyBasicDiagnostic(string[] sources, params DiagnosticResult[] expected)
+ {
+ VerifyDiagnostics(sources, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected);
+ }
+
+ ///
+ /// General method that gets a collection of actual diagnostics found in the source after the analyzer is run,
+ /// then verifies each of them.
+ ///
+ /// An array of strings to create source documents from to run the analyzers on
+ /// The language of the classes represented by the source strings
+ /// The analyzer to be run on the source code
+ /// DiagnosticResults that should appear after the analyzer is run on the sources
+ private void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expected)
+ {
+ var diagnostics = GetSortedDiagnostics(sources, language, analyzer);
+ VerifyDiagnosticResults(diagnostics, analyzer, expected);
+ }
+
+ #endregion
+
+ #region Actual comparisons and verifications
+ ///
+ /// Checks each of the actual Diagnostics found and compares them with the corresponding DiagnosticResult in the array of expected results.
+ /// Diagnostics are considered equal only if the DiagnosticResultLocation, Id, Severity, and Message of the DiagnosticResult match the actual diagnostic.
+ ///
+ /// The Diagnostics found by the compiler after running the analyzer on the source code
+ /// The analyzer that was being run on the sources
+ /// Diagnostic Results that should have appeared in the code
+ private static void VerifyDiagnosticResults(IEnumerable actualResults, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expectedResults)
+ {
+ int expectedCount = expectedResults.Count();
+ int actualCount = actualResults.Count();
+
+ if (expectedCount != actualCount)
+ {
+ string diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzer, actualResults.ToArray()) : " NONE.";
+
+ Assert.True(false,
+ string.Format("Mismatch between number of diagnostics returned, expected \"{0}\" actual \"{1}\"\r\n\r\nDiagnostics:\r\n{2}\r\n", expectedCount, actualCount, diagnosticsOutput));
+ }
+
+ for (int i = 0; i < expectedResults.Length; i++)
+ {
+ var actual = actualResults.ElementAt(i);
+ var expected = expectedResults[i];
+
+ if (expected.Line == -1 && expected.Column == -1)
+ {
+ if (actual.Location != Location.None)
+ {
+ Assert.True(false,
+ string.Format("Expected:\nA project diagnostic with No location\nActual:\n{0}",
+ FormatDiagnostics(analyzer, actual)));
+ }
+ }
+ else
+ {
+ VerifyDiagnosticLocation(analyzer, actual, actual.Location, expected.Locations.First());
+ var additionalLocations = actual.AdditionalLocations.ToArray();
+
+ if (additionalLocations.Length != expected.Locations.Length - 1)
+ {
+ Assert.True(false,
+ string.Format("Expected {0} additional locations but got {1} for Diagnostic:\r\n {2}\r\n",
+ expected.Locations.Length - 1, additionalLocations.Length,
+ FormatDiagnostics(analyzer, actual)));
+ }
+
+ for (int j = 0; j < additionalLocations.Length; ++j)
+ {
+ VerifyDiagnosticLocation(analyzer, actual, additionalLocations[j], expected.Locations[j + 1]);
+ }
+ }
+
+ if (actual.Id != expected.Id)
+ {
+ Assert.True(false,
+ string.Format("Expected diagnostic id to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
+ expected.Id, actual.Id, FormatDiagnostics(analyzer, actual)));
+ }
+
+ if (actual.Severity != expected.Severity)
+ {
+ Assert.True(false,
+ string.Format("Expected diagnostic severity to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
+ expected.Severity, actual.Severity, FormatDiagnostics(analyzer, actual)));
+ }
+
+ if (actual.GetMessage() != expected.Message)
+ {
+ Assert.True(false,
+ string.Format("Expected diagnostic message to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
+ expected.Message, actual.GetMessage(), FormatDiagnostics(analyzer, actual)));
+ }
+ }
+ }
+
+ ///
+ /// Helper method to VerifyDiagnosticResult that checks the location of a diagnostic and compares it with the location in the expected DiagnosticResult.
+ ///
+ /// The analyzer that was being run on the sources
+ /// The diagnostic that was found in the code
+ /// The Location of the Diagnostic found in the code
+ /// The DiagnosticResultLocation that should have been found
+ private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected)
+ {
+ var actualSpan = actual.GetLineSpan();
+
+ Assert.True(actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")),
+ string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
+ expected.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic)));
+
+ var actualLinePosition = actualSpan.StartLinePosition;
+
+ // Only check line position if there is an actual line in the real diagnostic
+ if (actualLinePosition.Line > 0)
+ {
+ if (actualLinePosition.Line + 1 != expected.Line)
+ {
+ Assert.True(false,
+ string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
+ expected.Line, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic)));
+ }
+ }
+
+ // Only check column position if there is an actual column position in the real diagnostic
+ if (actualLinePosition.Character > 0)
+ {
+ if (actualLinePosition.Character + 1 != expected.Column)
+ {
+ Assert.True(false,
+ string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
+ expected.Column, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic)));
+ }
+ }
+ }
+ #endregion
+
+ #region Formatting Diagnostics
+ ///
+ /// Helper method to format a Diagnostic into an easily readable string
+ ///
+ /// The analyzer that this verifier tests
+ /// The Diagnostics to be formatted
+ /// The Diagnostics formatted as a string
+ private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics)
+ {
+ var builder = new StringBuilder();
+ for (int i = 0; i < diagnostics.Length; ++i)
+ {
+ builder.AppendLine("// " + diagnostics[i].ToString());
+
+ var analyzerType = analyzer.GetType();
+ var rules = analyzer.SupportedDiagnostics;
+
+ foreach (var rule in rules)
+ {
+ if (rule != null && rule.Id == diagnostics[i].Id)
+ {
+ var location = diagnostics[i].Location;
+ if (location == Location.None)
+ {
+ builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, rule.Id);
+ }
+ else
+ {
+ Assert.True(location.IsInSource,
+ $"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[i]}\r\n");
+
+ string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt";
+ var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition;
+
+ builder.AppendFormat("{0}({1}, {2}, {3}.{4})",
+ resultMethodName,
+ linePosition.Line + 1,
+ linePosition.Character + 1,
+ analyzerType.Name,
+ rule.Id);
+ }
+
+ if (i != diagnostics.Length - 1)
+ {
+ builder.Append(',');
+ }
+
+ builder.AppendLine();
+ break;
+ }
+ }
+ }
+ return builder.ToString();
+ }
+ #endregion
+ }
+}
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.sln b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.sln
new file mode 100644
index 0000000000..a905ad7c35
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27703.2026
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Amazon.JSII.Analyzers", "Amazon.JSII.Analyzers\Amazon.JSII.Analyzers.csproj", "{E7BFF0E9-D8BF-4996-98B7-58334EA276D1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{B88CEA39-359B-45A0-86D6-0BC1FD284BBE}"
+ ProjectSection(SolutionItems) = preProject
+ Directory.Build.props = Directory.Build.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Amazon.JSII.Analyzers.UnitTests", "Amazon.JSII.Analyzers.UnitTests\Amazon.JSII.Analyzers.UnitTests.csproj", "{96CC0C0B-1D90-448F-9BFC-07CE93D2CE29}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E7BFF0E9-D8BF-4996-98B7-58334EA276D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E7BFF0E9-D8BF-4996-98B7-58334EA276D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E7BFF0E9-D8BF-4996-98B7-58334EA276D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E7BFF0E9-D8BF-4996-98B7-58334EA276D1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {96CC0C0B-1D90-448F-9BFC-07CE93D2CE29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {96CC0C0B-1D90-448F-9BFC-07CE93D2CE29}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {96CC0C0B-1D90-448F-9BFC-07CE93D2CE29}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {96CC0C0B-1D90-448F-9BFC-07CE93D2CE29}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7BD15A18-BE3A-4729-9B8C-570BF214C4CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7BD15A18-BE3A-4729-9B8C-570BF214C4CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7BD15A18-BE3A-4729-9B8C-570BF214C4CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7BD15A18-BE3A-4729-9B8C-570BF214C4CE}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {96CC0C0B-1D90-448F-9BFC-07CE93D2CE29} = {1F4EEFB5-9E4C-4464-9C3B-6729ABB0511E}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C5094D14-EAB4-4A0A-80A2-14748C28C057}
+ EndGlobalSection
+EndGlobal
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/Amazon.JSII.Analyzers.csproj b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/Amazon.JSII.Analyzers.csproj
new file mode 100644
index 0000000000..8e4ff80347
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/Amazon.JSII.Analyzers.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+ Amazon.JSII.Analyzers
+ .NET Roslyn Analyzers for JSII
+ netstandard2.0
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/JsiiOptionalAnalyzer.cs b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/JsiiOptionalAnalyzer.cs
new file mode 100644
index 0000000000..957a093d44
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/JsiiOptionalAnalyzer.cs
@@ -0,0 +1,139 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Amazon.JSII.Analyzers
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class JsiiOptionalAnalyzer : DiagnosticAnalyzer
+ {
+ private const string DiagnosticId = "JSII001";
+ private const string Title = "A required property is missing or null";
+ private const string MessageFormat = "The property is required and cannot be null";
+ private const string MessageFormatWithPropertyName = "The property {0} is required and cannot be null";
+ private const string Description = "The property is required and cannot be null";
+ private const string DescriptionWitPropertyName = "The property {0} is required and cannot be null";
+ private const string Category = "Jsii.Usage";
+
+ private static readonly DiagnosticDescriptor Rule =
+ new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description);
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.EnableConcurrentExecution();
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ObjectCreationExpression);
+ }
+
+ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
+ var typeInfo = context.SemanticModel.GetTypeInfo(objectCreation);
+ if (IsJsiiDatatype(typeInfo))
+ {
+ // If the newly created instance is a Jsii datatype [JsiiByValue]
+ // Get all the properties passed
+ var passedProperties = new HashSet();
+ foreach (var child in objectCreation.ChildNodes())
+ {
+ if (child.Kind() == SyntaxKind.ObjectInitializerExpression)
+ {
+ // This is an inline initialization
+ // Saving all the properties that are passed when initializing the props object
+ foreach (var passedProperty in child.ChildNodes().Where(n => n.Kind() == SyntaxKind.SimpleAssignmentExpression))
+ {
+ var props = passedProperty.ChildNodes().ToArray();
+ if (props.Length >= 2)
+ {
+ // Property = value
+ if (props[1].ToString() != "null") // value != null ?
+ {
+ var propName = props[0].ToString();
+ passedProperties.Add(propName);
+ }
+ }
+ }
+ }
+ }
+
+ // Parent.Parent.Parent = new Construct() instruction.
+ // #1 Parent = Argument
+ // #2 Parent = ArgumentList
+ // #3 Parent = ObjectCreationExpressionSyntax (if it exists).
+ var parentType = context.SemanticModel.GetTypeInfo(objectCreation.Parent.Parent.Parent);
+
+ // If the object initialization was an empty newProps() outside of a JsiiClass - We don't fail
+ if (passedProperties.Count == 0 && (parentType.Type == null || !IsJsiiClass(parentType)))
+ return;
+
+ // Get all the required properties on the prop object
+ var requiredProperties = typeInfo.Type.GetMembers()
+ .Where(m => m.Kind == SymbolKind.Property
+ && !IsJsiiOptionalProperty(m));
+ foreach (var requiredProperty in requiredProperties)
+ {
+ // The property in the props class IS NOT optional, check if it is passed as an argument.
+ if (!passedProperties.Contains(requiredProperty.Name))
+ {
+ // This property IS REQUIRED and was not passed in the arguments. Raising an error
+ var rule = new DiagnosticDescriptor(DiagnosticId,
+ Title,
+ string.Format(MessageFormatWithPropertyName, requiredProperty.Name),
+ Category,
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: string.Format(DescriptionWitPropertyName, requiredProperty.Name));
+ context.ReportDiagnostic(Diagnostic.Create(rule, context.Node.GetLocation()));
+ }
+ }
+ }
+ }
+
+ ///
+ /// Checks if the TypeInfo is related to a Jsii class
+ ///
+ ///
+ /// This is done by checking for the [JsiiClass] attribute
+ ///
+ /// The TypeInfo object to check for
+ /// true if the TypeInfo is related to a Jsii class, false otherwise
+ private static bool IsJsiiClass(TypeInfo typeInfo)
+ {
+ var typeAttributes = typeInfo.Type.GetAttributes().ToArray();
+ return typeAttributes.Any(a => a.AttributeClass.Name == "JsiiClassAttribute");
+ }
+
+ ///
+ /// Checks if the TypeInfo is related to a Jsii datatype
+ ///
+ ///
+ /// This is done by checking for the [JsiiByValueAttribute] attribute
+ ///
+ /// The TypeInfo object to check for
+ /// true if the TypeInfo is related to a Jsii datatype, false otherwise
+ private static bool IsJsiiDatatype(TypeInfo typeInfo)
+ {
+ var typeAttributes = typeInfo.Type.GetAttributes().ToArray();
+ return typeAttributes.Any(a => a.AttributeClass.Name == "JsiiByValueAttribute");
+ }
+
+ ///
+ /// Checks if the property is optional for jsii
+ ///
+ ///
+ /// This is done by checking for the [JsiiOptionalAttribute] attribute
+ ///
+ /// The property to check for
+ /// true if the property is optional, false otherwise
+ private static bool IsJsiiOptionalProperty(ISymbol property)
+ {
+ return property.GetAttributes().Any(a => a.AttributeClass.Name == "JsiiOptionalAttribute");
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/tools/install.ps1 b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/tools/install.ps1
new file mode 100644
index 0000000000..c1c3d88223
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/tools/install.ps1
@@ -0,0 +1,58 @@
+param($installPath, $toolsPath, $package, $project)
+
+if($project.Object.SupportsPackageDependencyResolution)
+{
+ if($project.Object.SupportsPackageDependencyResolution())
+ {
+ # Do not install analyzers via install.ps1, instead let the project system handle it.
+ return
+ }
+}
+
+$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ if (Test-Path $analyzersPath)
+ {
+ # Install the language agnostic analyzers.
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+
+# $project.Type gives the language name like (C# or VB.NET)
+$languageFolder = ""
+if($project.Type -eq "C#")
+{
+ $languageFolder = "cs"
+}
+if($project.Type -eq "VB.NET")
+{
+ $languageFolder = "vb"
+}
+if($languageFolder -eq "")
+{
+ return
+}
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Install language specific analyzers.
+ $languageAnalyzersPath = join-path $analyzersPath $languageFolder
+ if (Test-Path $languageAnalyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/tools/uninstall.ps1 b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/tools/uninstall.ps1
new file mode 100644
index 0000000000..829d26efa9
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/src/Amazon.JSII.Analyzers/tools/uninstall.ps1
@@ -0,0 +1,65 @@
+param($installPath, $toolsPath, $package, $project)
+
+if($project.Object.SupportsPackageDependencyResolution)
+{
+ if($project.Object.SupportsPackageDependencyResolution())
+ {
+ # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it.
+ return
+ }
+}
+
+$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Uninstall the language agnostic analyzers.
+ if (Test-Path $analyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+
+# $project.Type gives the language name like (C# or VB.NET)
+$languageFolder = ""
+if($project.Type -eq "C#")
+{
+ $languageFolder = "cs"
+}
+if($project.Type -eq "VB.NET")
+{
+ $languageFolder = "vb"
+}
+if($languageFolder -eq ""),
+{
+ return
+}
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Uninstall language specific analyzers.
+ $languageAnalyzersPath = join-path $analyzersPath $languageFolder
+ if (Test-Path $languageAnalyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ try
+ {
+ $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
+ }
+ catch
+ {
+
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/jsii-dotnet-analyzers/test.sh b/packages/jsii-dotnet-analyzers/test.sh
new file mode 100644
index 0000000000..5338fc26dc
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -euo pipefail
+
+# Run unit tests
+echo "Running library unit tests"
+dotnet test -c Release ./src/Amazon.JSII.Analyzers.UnitTests
diff --git a/packages/jsii-dotnet-analyzers/tsconfig.json b/packages/jsii-dotnet-analyzers/tsconfig.json
new file mode 100644
index 0000000000..6f8f29cf64
--- /dev/null
+++ b/packages/jsii-dotnet-analyzers/tsconfig.json
@@ -0,0 +1,54 @@
+{
+ "compilerOptions": {
+ /* Basic Options */
+ "target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
+ "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
+ "lib": ["es2016", "es2017.object", "es2017.string"], /* Specify library files to be included in the compilation: */
+ // "allowJs": true, /* Allow javascript files to be compiled. */
+ // "checkJs": true, /* Report errors in .js files. */
+ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
+ "declaration": true, /* Generates corresponding '.d.ts' file. */
+ // "sourceMap": true, /* Generates corresponding '.map' file. */
+ // "outFile": "./", /* Concatenate and emit output to single file. */
+ // "outDir": "./", /* Redirect output structure to the directory. */
+ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
+ // "removeComments": true, /* Do not emit comments to output. */
+ // "noEmit": true, /* Do not emit outputs. */
+ // "importHelpers": true, /* Import emit helpers from 'tslib'. */
+ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
+ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
+
+ /* Strict Type-Checking Options */
+ "strict": true, /* Enable all strict type-checking options. */
+ "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
+ "strictNullChecks": true, /* Enable strict null checks. */
+ "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
+ "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
+ "strictPropertyInitialization": false, /* DO NOT Raise error on class attribute not initialized by constructor. */
+
+ /* Additional Checks */
+ "noUnusedLocals": true, /* Report errors on unused locals. */
+ "noUnusedParameters": true, /* Report errors on unused parameters. */
+ "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
+ "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
+
+ /* Module Resolution Options */
+ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
+ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
+ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
+ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
+ // "typeRoots": [], /* List of folders to include type definitions from. */
+ // "types": [], /* Type declaration files to be included in compilation. */
+ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
+
+ /* Source Map Options */
+ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
+ // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": false, /* Emit a single file with source maps instead of having a separate file. */
+ // "inlineSources": false, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
+
+ /* Experimental Options */
+ "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */
+ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
+ }
+}
diff --git a/packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime/Amazon.JSII.Runtime.csproj b/packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime/Amazon.JSII.Runtime.csproj
index a157ba16b6..761a89bc56 100644
--- a/packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime/Amazon.JSII.Runtime.csproj
+++ b/packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime/Amazon.JSII.Runtime.csproj
@@ -14,10 +14,9 @@
-
+
-
diff --git a/packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime/Deputy/JsiiOptionalAttribute.cs b/packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime/Deputy/JsiiOptionalAttribute.cs
new file mode 100644
index 0000000000..1102879073
--- /dev/null
+++ b/packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime/Deputy/JsiiOptionalAttribute.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Amazon.JSII.Runtime.Deputy
+{
+ ///
+ /// Flags a property as optional.
+ /// This is used by the jsii-dotnet-analyzers package to emit errors
+ /// on required properties that are missing.
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class JsiiOptionalAttribute : Attribute
+ {
+ }
+}
\ No newline at end of file
diff --git a/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts b/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts
index e3e6061256..ff4a2ff95a 100644
--- a/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts
+++ b/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts
@@ -644,6 +644,9 @@ export class DotNetGenerator extends Generator {
const propName = this.nameutils.convertPropertyName(prop.name);
this.dotnetDocGenerator.emitDocs(prop);
+ if (prop.optional) {
+ this.code.line('[JsiiOptional]');
+ }
this.dotnetRuntimeGenerator.emitAttributesForProperty(prop, datatype);
let isOverrideKeyWord = '';
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/MyFirstStruct.cs b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/MyFirstStruct.cs
index c36847573d..2c2b7cdecb 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/MyFirstStruct.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/MyFirstStruct.cs
@@ -36,6 +36,7 @@ public string Astring
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "firstOptional", typeJson: "{\"collection\":{\"elementtype\":{\"primitive\":\"string\"},\"kind\":\"array\"}}", isOptional: true, isOverride: true)]
[System.Obsolete()]
public string[] FirstOptional
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/MyFirstStructProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/MyFirstStructProxy.cs
index 8c15dae980..f0ac0931da 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/MyFirstStructProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/MyFirstStructProxy.cs
@@ -39,6 +39,7 @@ public string Astring
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "firstOptional", typeJson: "{\"collection\":{\"elementtype\":{\"primitive\":\"string\"},\"kind\":\"array\"}}", isOptional: true)]
[System.Obsolete()]
public string[] FirstOptional
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/StructWithOnlyOptionals.cs b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/StructWithOnlyOptionals.cs
index d82c4b4553..2635cc9ab5 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/StructWithOnlyOptionals.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/StructWithOnlyOptionals.cs
@@ -13,6 +13,7 @@ public class StructWithOnlyOptionals : Amazon.JSII.Tests.CalculatorNamespace.Lib
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "optional1", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
[System.Obsolete()]
public string Optional1
@@ -24,6 +25,7 @@ public string Optional1
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "optional2", typeJson: "{\"primitive\":\"number\"}", isOptional: true, isOverride: true)]
[System.Obsolete()]
public double? Optional2
@@ -35,6 +37,7 @@ public double? Optional2
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "optional3", typeJson: "{\"primitive\":\"boolean\"}", isOptional: true, isOverride: true)]
[System.Obsolete()]
public bool? Optional3
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/StructWithOnlyOptionalsProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/StructWithOnlyOptionalsProxy.cs
index 2034bc7b4c..3074e21fbe 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/StructWithOnlyOptionalsProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/StructWithOnlyOptionalsProxy.cs
@@ -18,6 +18,7 @@ private StructWithOnlyOptionalsProxy(ByRefValue reference): base(reference)
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "optional1", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
[System.Obsolete()]
public string Optional1
@@ -28,6 +29,7 @@ public string Optional1
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "optional2", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
[System.Obsolete()]
public double? Optional2
@@ -38,6 +40,7 @@ public double? Optional2
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "optional3", typeJson: "{\"primitive\":\"boolean\"}", isOptional: true)]
[System.Obsolete()]
public bool? Optional3
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AllTypes.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AllTypes.cs
index b5b70f1476..3fa26cb700 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AllTypes.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AllTypes.cs
@@ -232,6 +232,7 @@ public virtual object UnknownProperty
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "optionalEnumValue", typeJson: "{\"fqn\":\"jsii-calc.StringEnum\"}", isOptional: true)]
public virtual Amazon.JSII.Tests.CalculatorNamespace.StringEnum? OptionalEnumValue
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Calculator.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Calculator.cs
index 3b38368237..27f02c06ab 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Calculator.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Calculator.cs
@@ -121,6 +121,7 @@ public virtual Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.Value_ Curr
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "maxValue", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
public virtual double? MaxValue
{
@@ -132,6 +133,7 @@ public virtual double? MaxValue
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "unionProperty", typeJson: "{\"union\":{\"types\":[{\"fqn\":\"jsii-calc.Add\"},{\"fqn\":\"jsii-calc.Multiply\"},{\"fqn\":\"jsii-calc.Power\"}]}}", isOptional: true)]
public virtual object UnionProperty
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/CalculatorProps.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/CalculatorProps.cs
index e6fe4c9cab..d9ee88181e 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/CalculatorProps.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/CalculatorProps.cs
@@ -12,6 +12,7 @@ public class CalculatorProps : Amazon.JSII.Tests.CalculatorNamespace.ICalculator
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "initialValue", typeJson: "{\"primitive\":\"number\"}", isOptional: true, isOverride: true)]
public double? InitialValue
{
@@ -22,6 +23,7 @@ public double? InitialValue
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "maximumValue", typeJson: "{\"primitive\":\"number\"}", isOptional: true, isOverride: true)]
public double? MaximumValue
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/CalculatorPropsProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/CalculatorPropsProxy.cs
index 9582baa158..f6d967c1e1 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/CalculatorPropsProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/CalculatorPropsProxy.cs
@@ -16,6 +16,7 @@ private CalculatorPropsProxy(ByRefValue reference): base(reference)
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "initialValue", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
public double? InitialValue
{
@@ -25,6 +26,7 @@ public double? InitialValue
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "maximumValue", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
public double? MaximumValue
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DefaultedConstructorArgument.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DefaultedConstructorArgument.cs
index 15798beccd..929d118bf4 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DefaultedConstructorArgument.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DefaultedConstructorArgument.cs
@@ -44,6 +44,7 @@ public virtual System.DateTime Arg3
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "arg2", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public virtual string Arg2
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DeprecatedClass.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DeprecatedClass.cs
index eb025e0de6..b9c7382590 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DeprecatedClass.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DeprecatedClass.cs
@@ -50,6 +50,7 @@ public virtual string ReadonlyProperty
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "mutableProperty", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
[System.Obsolete("shouldn't have been mutable")]
public virtual double? MutableProperty
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DerivedStruct.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DerivedStruct.cs
index 5d82d1ad0a..2b4a1ada28 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DerivedStruct.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DerivedStruct.cs
@@ -44,6 +44,7 @@ public Amazon.JSII.Tests.CalculatorNamespace.DoubleTrouble NonPrimitive
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "anotherOptional", typeJson: "{\"collection\":{\"elementtype\":{\"fqn\":\"@scope/jsii-calc-lib.Value\"},\"kind\":\"map\"}}", isOptional: true, isOverride: true)]
public System.Collections.Generic.IDictionary AnotherOptional
{
@@ -54,6 +55,7 @@ public Amazon.JSII.Tests.CalculatorNamespace.DoubleTrouble NonPrimitive
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "optionalAny", typeJson: "{\"primitive\":\"any\"}", isOptional: true, isOverride: true)]
public object OptionalAny
{
@@ -64,6 +66,7 @@ public object OptionalAny
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "optionalArray", typeJson: "{\"collection\":{\"elementtype\":{\"primitive\":\"string\"},\"kind\":\"array\"}}", isOptional: true, isOverride: true)]
public string[] OptionalArray
{
@@ -98,6 +101,7 @@ public string Astring
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "firstOptional", typeJson: "{\"collection\":{\"elementtype\":{\"primitive\":\"string\"},\"kind\":\"array\"}}", isOptional: true, isOverride: true)]
[System.Obsolete()]
public string[] FirstOptional
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DerivedStructProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DerivedStructProxy.cs
index da0836ab8f..0b16f241bd 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DerivedStructProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DerivedStructProxy.cs
@@ -45,6 +45,7 @@ public Amazon.JSII.Tests.CalculatorNamespace.DoubleTrouble NonPrimitive
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "anotherOptional", typeJson: "{\"collection\":{\"elementtype\":{\"fqn\":\"@scope/jsii-calc-lib.Value\"},\"kind\":\"map\"}}", isOptional: true)]
public System.Collections.Generic.IDictionary AnotherOptional
{
@@ -54,6 +55,7 @@ public Amazon.JSII.Tests.CalculatorNamespace.DoubleTrouble NonPrimitive
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "optionalAny", typeJson: "{\"primitive\":\"any\"}", isOptional: true)]
public object OptionalAny
{
@@ -63,6 +65,7 @@ public object OptionalAny
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "optionalArray", typeJson: "{\"collection\":{\"elementtype\":{\"primitive\":\"string\"},\"kind\":\"array\"}}", isOptional: true)]
public string[] OptionalArray
{
@@ -94,6 +97,7 @@ public string Astring
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "firstOptional", typeJson: "{\"collection\":{\"elementtype\":{\"primitive\":\"string\"},\"kind\":\"array\"}}", isOptional: true)]
[System.Obsolete()]
public string[] FirstOptional
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/EraseUndefinedHashValuesOptions.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/EraseUndefinedHashValuesOptions.cs
index 4ed1aa61a3..fbbd4264a9 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/EraseUndefinedHashValuesOptions.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/EraseUndefinedHashValuesOptions.cs
@@ -11,6 +11,7 @@ public class EraseUndefinedHashValuesOptions : Amazon.JSII.Tests.CalculatorNames
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "option1", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string Option1
{
@@ -21,6 +22,7 @@ public string Option1
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "option2", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string Option2
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/EraseUndefinedHashValuesOptionsProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/EraseUndefinedHashValuesOptionsProxy.cs
index 84dc19301f..9faeb33e5d 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/EraseUndefinedHashValuesOptionsProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/EraseUndefinedHashValuesOptionsProxy.cs
@@ -15,6 +15,7 @@ private EraseUndefinedHashValuesOptionsProxy(ByRefValue reference): base(referen
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "option1", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string Option1
{
@@ -24,6 +25,7 @@ public string Option1
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "option2", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string Option2
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ExperimentalClass.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ExperimentalClass.cs
index 47db920320..9a87527611 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ExperimentalClass.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ExperimentalClass.cs
@@ -44,6 +44,7 @@ public virtual string ReadonlyProperty
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "mutableProperty", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
public virtual double? MutableProperty
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Greetee.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Greetee.cs
index ccd8effa38..19d064df54 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Greetee.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Greetee.cs
@@ -15,6 +15,7 @@ public class Greetee : Amazon.JSII.Tests.CalculatorNamespace.IGreetee
/// world
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "name", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string Name
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/GreeteeProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/GreeteeProxy.cs
index bf9232f9ab..181ebd82c7 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/GreeteeProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/GreeteeProxy.cs
@@ -19,6 +19,7 @@ private GreeteeProxy(ByRefValue reference): base(reference)
/// world
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "name", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string Name
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IDeprecatedInterfaceProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IDeprecatedInterfaceProxy.cs
index cca4d9bd98..f1d60e948c 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IDeprecatedInterfaceProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IDeprecatedInterfaceProxy.cs
@@ -16,6 +16,7 @@ private IDeprecatedInterfaceProxy(ByRefValue reference): base(reference)
///
/// stability: Deprecated
///
+ [JsiiOptional]
[JsiiProperty(name: "mutableProperty", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
[System.Obsolete("could be better")]
public double? MutableProperty
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IExperimentalInterfaceProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IExperimentalInterfaceProxy.cs
index 5480a314be..09b159632b 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IExperimentalInterfaceProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IExperimentalInterfaceProxy.cs
@@ -15,6 +15,7 @@ private IExperimentalInterfaceProxy(ByRefValue reference): base(reference)
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "mutableProperty", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
public double? MutableProperty
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IStableInterfaceProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IStableInterfaceProxy.cs
index d951e4593f..5b7b4f6f98 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IStableInterfaceProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IStableInterfaceProxy.cs
@@ -15,6 +15,7 @@ private IStableInterfaceProxy(ByRefValue reference): base(reference)
///
/// stability: Stable
///
+ [JsiiOptional]
[JsiiProperty(name: "mutableProperty", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
public double? MutableProperty
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/InterfaceInNamespaceIncludesClasses/Foo.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/InterfaceInNamespaceIncludesClasses/Foo.cs
index 4f9fab38a5..50e5611192 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/InterfaceInNamespaceIncludesClasses/Foo.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/InterfaceInNamespaceIncludesClasses/Foo.cs
@@ -23,6 +23,7 @@ protected Foo(DeputyProps props): base(props)
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "bar", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public virtual string Bar
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/JsiiAgent_.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/JsiiAgent_.cs
index 2759a17388..9208e398f1 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/JsiiAgent_.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/JsiiAgent_.cs
@@ -25,6 +25,7 @@ protected JsiiAgent_(DeputyProps props): base(props)
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "jsiiAgent", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public static string JsiiAgent
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/LoadBalancedFargateServiceProps.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/LoadBalancedFargateServiceProps.cs
index 25c3f9f42f..3f4ad3c875 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/LoadBalancedFargateServiceProps.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/LoadBalancedFargateServiceProps.cs
@@ -16,6 +16,7 @@ public class LoadBalancedFargateServiceProps : Amazon.JSII.Tests.CalculatorNames
/// 80
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "containerPort", typeJson: "{\"primitive\":\"number\"}", isOptional: true, isOverride: true)]
public double? ContainerPort
{
@@ -30,6 +31,7 @@ public double? ContainerPort
/// 256
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "cpu", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string Cpu
{
@@ -57,6 +59,7 @@ public string Cpu
/// 512
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "memoryMiB", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string MemoryMiB
{
@@ -70,6 +73,7 @@ public string MemoryMiB
/// true
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "publicLoadBalancer", typeJson: "{\"primitive\":\"boolean\"}", isOptional: true, isOverride: true)]
public bool? PublicLoadBalancer
{
@@ -83,6 +87,7 @@ public bool? PublicLoadBalancer
/// false
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "publicTasks", typeJson: "{\"primitive\":\"boolean\"}", isOptional: true, isOverride: true)]
public bool? PublicTasks
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/LoadBalancedFargateServicePropsProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/LoadBalancedFargateServicePropsProxy.cs
index 56607197f6..c68b635314 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/LoadBalancedFargateServicePropsProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/LoadBalancedFargateServicePropsProxy.cs
@@ -20,6 +20,7 @@ private LoadBalancedFargateServicePropsProxy(ByRefValue reference): base(referen
/// 80
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "containerPort", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
public double? ContainerPort
{
@@ -33,6 +34,7 @@ public double? ContainerPort
/// 256
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "cpu", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string Cpu
{
@@ -59,6 +61,7 @@ public string Cpu
/// 512
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "memoryMiB", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string MemoryMiB
{
@@ -71,6 +74,7 @@ public string MemoryMiB
/// true
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "publicLoadBalancer", typeJson: "{\"primitive\":\"boolean\"}", isOptional: true)]
public bool? PublicLoadBalancer
{
@@ -83,6 +87,7 @@ public bool? PublicLoadBalancer
/// false
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "publicTasks", typeJson: "{\"primitive\":\"boolean\"}", isOptional: true)]
public bool? PublicTasks
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefined.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefined.cs
index 897d15eda1..4b3cd58211 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefined.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefined.cs
@@ -54,6 +54,7 @@ public virtual void VerifyPropertyIsUndefined()
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "changeMeToUndefined", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public virtual string ChangeMeToUndefined
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefinedData.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefinedData.cs
index 9bf866fef9..a88fd52f43 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefinedData.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefinedData.cs
@@ -21,6 +21,7 @@ public object[] ArrayWithThreeElementsAndUndefinedAsSecondArgument
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "thisShouldBeUndefined", typeJson: "{\"primitive\":\"any\"}", isOptional: true, isOverride: true)]
public object ThisShouldBeUndefined
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefinedDataProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefinedDataProxy.cs
index f409bb3aac..591bd0cf89 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefinedDataProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/NullShouldBeTreatedAsUndefinedDataProxy.cs
@@ -24,6 +24,7 @@ public object[] ArrayWithThreeElementsAndUndefinedAsSecondArgument
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "thisShouldBeUndefined", typeJson: "{\"primitive\":\"any\"}", isOptional: true)]
public object ThisShouldBeUndefined
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalConstructorArgument.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalConstructorArgument.cs
index 3cbbc1ac03..8a36aa1250 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalConstructorArgument.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalConstructorArgument.cs
@@ -44,6 +44,7 @@ public virtual string Arg2
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "arg3", typeJson: "{\"primitive\":\"date\"}", isOptional: true)]
public virtual System.DateTime? Arg3
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStruct.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStruct.cs
index 685ed0bc75..0e55a0c4af 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStruct.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStruct.cs
@@ -11,6 +11,7 @@ public class OptionalStruct : Amazon.JSII.Tests.CalculatorNamespace.IOptionalStr
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "field", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string Field
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStructConsumer.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStructConsumer.cs
index 7dac995a47..c473601635 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStructConsumer.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStructConsumer.cs
@@ -35,6 +35,7 @@ public virtual bool ParameterWasUndefined
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "fieldValue", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public virtual string FieldValue
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStructProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStructProxy.cs
index d831d90c68..9f26565881 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStructProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/OptionalStructProxy.cs
@@ -15,6 +15,7 @@ private OptionalStructProxy(ByRefValue reference): base(reference)
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "field", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string Field
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ReferenceEnumFromScopedPackage.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ReferenceEnumFromScopedPackage.cs
index d0822350e9..f9aa6e4946 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ReferenceEnumFromScopedPackage.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ReferenceEnumFromScopedPackage.cs
@@ -42,6 +42,7 @@ public virtual void SaveFoo(Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.E
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "foo", typeJson: "{\"fqn\":\"@scope/jsii-calc-lib.EnumFromScopedModule\"}", isOptional: true)]
public virtual Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.EnumFromScopedModule? Foo
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/SecondLevelStruct.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/SecondLevelStruct.cs
index 3dbc4c062b..7c40a353be 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/SecondLevelStruct.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/SecondLevelStruct.cs
@@ -23,6 +23,7 @@ public string DeeperRequiredProp
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "deeperOptionalProp", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string DeeperOptionalProp
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/SecondLevelStructProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/SecondLevelStructProxy.cs
index fa23095a8f..79d73c80f6 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/SecondLevelStructProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/SecondLevelStructProxy.cs
@@ -26,6 +26,7 @@ public string DeeperRequiredProp
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "deeperOptionalProp", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string DeeperOptionalProp
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StableClass.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StableClass.cs
index eea996a83f..606e61050a 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StableClass.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StableClass.cs
@@ -44,6 +44,7 @@ public virtual string ReadonlyProperty
///
/// stability: Stable
///
+ [JsiiOptional]
[JsiiProperty(name: "mutableProperty", typeJson: "{\"primitive\":\"number\"}", isOptional: true)]
public virtual double? MutableProperty
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructWithJavaReservedWords.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructWithJavaReservedWords.cs
index 3067e8e87e..438bb39094 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructWithJavaReservedWords.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructWithJavaReservedWords.cs
@@ -21,6 +21,7 @@ public string Default
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "assert", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string Assert
{
@@ -31,6 +32,7 @@ public string Assert
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "result", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string Result
{
@@ -41,6 +43,7 @@ public string Result
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "that", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string That
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructWithJavaReservedWordsProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructWithJavaReservedWordsProxy.cs
index 4ea4f6fa23..b705625b0d 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructWithJavaReservedWordsProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructWithJavaReservedWordsProxy.cs
@@ -24,6 +24,7 @@ public string Default
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "assert", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string Assert
{
@@ -33,6 +34,7 @@ public string Assert
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "result", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string Result
{
@@ -42,6 +44,7 @@ public string Result
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "that", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string That
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/TopLevelStruct.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/TopLevelStruct.cs
index 107d1c91e6..41725b0da4 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/TopLevelStruct.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/TopLevelStruct.cs
@@ -34,6 +34,7 @@ public object SecondLevel
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "optional", typeJson: "{\"primitive\":\"string\"}", isOptional: true, isOverride: true)]
public string Optional
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/TopLevelStructProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/TopLevelStructProxy.cs
index 274ccb3ce9..7d683eb598 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/TopLevelStructProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/TopLevelStructProxy.cs
@@ -36,6 +36,7 @@ public object SecondLevel
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "optional", typeJson: "{\"primitive\":\"string\"}", isOptional: true)]
public string Optional
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/UnionProperties.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/UnionProperties.cs
index 71b5875699..606c4ac6b5 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/UnionProperties.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/UnionProperties.cs
@@ -21,6 +21,7 @@ public object Bar
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "foo", typeJson: "{\"union\":{\"types\":[{\"primitive\":\"string\"},{\"primitive\":\"number\"}]}}", isOptional: true, isOverride: true)]
public object Foo
{
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/UnionPropertiesProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/UnionPropertiesProxy.cs
index 41c79d7eea..cb853c0301 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/UnionPropertiesProxy.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/UnionPropertiesProxy.cs
@@ -24,6 +24,7 @@ public object Bar
///
/// stability: Experimental
///
+ [JsiiOptional]
[JsiiProperty(name: "foo", typeJson: "{\"union\":{\"types\":[{\"primitive\":\"string\"},{\"primitive\":\"number\"}]}}", isOptional: true)]
public object Foo
{