diff --git a/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj b/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj index 7aabace..3768715 100644 --- a/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj +++ b/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netcoreapp2.1;netcoreapp3.0 + netstandard2.0;netstandard2.1;netcoreapp2.1;netcoreapp3.0 Capnp 8.0 Enable diff --git a/Capnp.Net.Runtime/CapnpSerializable.cs b/Capnp.Net.Runtime/CapnpSerializable.cs index 520e737..81b86b6 100644 --- a/Capnp.Net.Runtime/CapnpSerializable.cs +++ b/Capnp.Net.Runtime/CapnpSerializable.cs @@ -148,22 +148,24 @@ namespace Capnp /// Type implementing . The type must must have a public parameterless constructor. /// A capability interface ( for further explanation) /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// whereby T is one of the things listed here. + /// IReadOnlyList{Boolean} + /// IReadOnlyList{SByte}" + /// IReadOnlyList{Byte}" + /// IReadOnlyList{Int16}" + /// IReadOnlyList{UInt16}" + /// IReadOnlyList{Int32}" + /// IReadOnlyList{UInt32}" + /// IReadOnlyList{Int64}" + /// IReadOnlyList{UInt64}" + /// IReadOnlyList{Single}" + /// IReadOnlyList{Double}" + /// IReadOnlyList{T} whereby T is one of the things listed here. /// /// - /// - /// + /// deserializer state to construct from + /// The domain object instance. Nullability note: The returned reference will be null if (and only if) is a capability interface and + /// represents the nil object (obtained from a null pointer). For all other types, when the state is nil, + /// the method still constructs a valid but "empty" object instance (such as domain object without any properties set, empty string, empty list etc.) public static T? Create(DeserializerState state) where T: class { diff --git a/Capnp.Net.Runtime/DeserializerState.cs b/Capnp.Net.Runtime/DeserializerState.cs index 89cd0c4..8dd7ce8 100644 --- a/Capnp.Net.Runtime/DeserializerState.cs +++ b/Capnp.Net.Runtime/DeserializerState.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Capnp { @@ -572,6 +573,7 @@ namespace Capnp /// negative index /// state does not represent a struct, invalid pointer, /// non-list-of-bytes pointer, traversal limit exceeded + [return: NotNullIfNotNull("defaultText")] public string? ReadText(int index, string? defaultText = null) { return StructReadPointer(index).RequireList().CastText() ?? defaultText; diff --git a/Capnp.Net.Runtime/DynamicSerializerState.cs b/Capnp.Net.Runtime/DynamicSerializerState.cs index 2bf2be2..679a59f 100644 --- a/Capnp.Net.Runtime/DynamicSerializerState.cs +++ b/Capnp.Net.Runtime/DynamicSerializerState.cs @@ -144,7 +144,7 @@ namespace Capnp /// A ]]> whereby each list item is one of the things listed here. /// /// - public void SetObject(object obj) + public void SetObject(object? obj) { switch (obj) { diff --git a/Capnp.Net.Runtime/ListOfBitsSerializer.cs b/Capnp.Net.Runtime/ListOfBitsSerializer.cs index 0719d32..6c00c7a 100644 --- a/Capnp.Net.Runtime/ListOfBitsSerializer.cs +++ b/Capnp.Net.Runtime/ListOfBitsSerializer.cs @@ -73,7 +73,7 @@ namespace Capnp /// List content. Can be null in which case the list is simply not initialized. /// The list was already initialized /// More than 2^29-1 items. - public void Init(IReadOnlyList items) + public void Init(IReadOnlyList? items) { if (items == null) { diff --git a/Capnp.Net.Runtime/ListOfCapsSerializer.cs b/Capnp.Net.Runtime/ListOfCapsSerializer.cs index 7cc94e0..eef1d7c 100644 --- a/Capnp.Net.Runtime/ListOfCapsSerializer.cs +++ b/Capnp.Net.Runtime/ListOfCapsSerializer.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Capnp { @@ -10,7 +11,7 @@ namespace Capnp /// Capability interface public class ListOfCapsSerializer : SerializerState, - IReadOnlyList + IReadOnlyList where T : class { /// @@ -31,6 +32,7 @@ namespace Capnp /// Proxy object of capability at given element index /// List was not initialized, or attempting to overwrite an already set element. /// is out of range. + [AllowNull] public T this[int index] { get => (Rpc.CapabilityReflection.CreateProxy(DecodeCapPointer(index)) as T)!; @@ -42,10 +44,7 @@ namespace Capnp if (index < 0 || index >= RawData.Length) throw new IndexOutOfRangeException("index out of range"); - uint id = ProvideCapability(value); - WirePointer ptr = default; - ptr.SetCapability(id); - RawData[index] = id; + RawData[index] = ProvideCapability(value); } } @@ -66,7 +65,7 @@ namespace Capnp /// List content. Can be null in which case the list is simply not initialized. /// The list was already initialized /// More than 2^29-1 items. - public void Init(IReadOnlyList caps) + public void Init(IReadOnlyList? caps) { if (caps == null) { diff --git a/Capnp.Net.Runtime/ListOfPointersSerializer.cs b/Capnp.Net.Runtime/ListOfPointersSerializer.cs index 66fc61c..b6b42fe 100644 --- a/Capnp.Net.Runtime/ListOfPointersSerializer.cs +++ b/Capnp.Net.Runtime/ListOfPointersSerializer.cs @@ -92,7 +92,7 @@ namespace Capnp /// Serialization action to transfer a particular item into the serializer state. /// The list was already initialized /// More than 2^29-1 items. - public void Init(IReadOnlyList items, Action init) + public void Init(IReadOnlyList? items, Action init) { if (items == null) { diff --git a/Capnp.Net.Runtime/ListOfPrimitivesSerializer.cs b/Capnp.Net.Runtime/ListOfPrimitivesSerializer.cs index 5258543..b70a3a9 100644 --- a/Capnp.Net.Runtime/ListOfPrimitivesSerializer.cs +++ b/Capnp.Net.Runtime/ListOfPrimitivesSerializer.cs @@ -69,7 +69,7 @@ namespace Capnp /// List content. Can be null in which case the list is simply not initialized. /// The list was already initialized /// More than 2^29-1 items. - public void Init(IReadOnlyList items) + public void Init(IReadOnlyList? items) { if (items == null) { diff --git a/Capnp.Net.Runtime/ListOfStructsSerializer.cs b/Capnp.Net.Runtime/ListOfStructsSerializer.cs index be7c00e..e84441b 100644 --- a/Capnp.Net.Runtime/ListOfStructsSerializer.cs +++ b/Capnp.Net.Runtime/ListOfStructsSerializer.cs @@ -72,7 +72,7 @@ namespace Capnp /// Serialization action to transfer a particular item into the serializer state. /// The list was already initialized /// More than 2^29-1 items. - public void Init(IReadOnlyList items, Action init) + public void Init(IReadOnlyList? items, Action init) { if (items == null) { diff --git a/Capnp.Net.Runtime/ListOfTextSerializer.cs b/Capnp.Net.Runtime/ListOfTextSerializer.cs index d737c45..5bd63e7 100644 --- a/Capnp.Net.Runtime/ListOfTextSerializer.cs +++ b/Capnp.Net.Runtime/ListOfTextSerializer.cs @@ -88,7 +88,7 @@ namespace Capnp /// List content. Can be null in which case the list is simply not initialized. /// The list was already initialized /// More than 2^29-1 items, or the UTF-8 encoding of an individual string requires more than 2^29-2 bytes. - public void Init(IReadOnlyList items) + public void Init(IReadOnlyList? items) { if (items == null) { diff --git a/Capnp.Net.Runtime/NullableAttributes.cs b/Capnp.Net.Runtime/NullableAttributes.cs new file mode 100644 index 0000000..4e65208 --- /dev/null +++ b/Capnp.Net.Runtime/NullableAttributes.cs @@ -0,0 +1,140 @@ +#pragma warning disable MA0048 // File name must match type name +#define INTERNAL_NULLABLE_ATTRIBUTES +#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48 + +// https://github.com/dotnet/corefx/blob/48363ac826ccf66fbe31a5dcb1dc2aab9a7dd768/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/NullableAttributes.cs + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class AllowNullAttribute : Attribute + { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class DisallowNullAttribute : Attribute + { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class MaybeNullAttribute : Attribute + { } + + /// Specifies that an output will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class NotNullAttribute : Attribute + { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class DoesNotReturnAttribute : Attribute + { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } +} +#endif \ No newline at end of file diff --git a/Capnp.Net.Runtime/SerializerState.cs b/Capnp.Net.Runtime/SerializerState.cs index ac502ca..4490ada 100644 --- a/Capnp.Net.Runtime/SerializerState.cs +++ b/Capnp.Net.Runtime/SerializerState.cs @@ -882,7 +882,7 @@ namespace Capnp /// Object at given position was already set. /// /// is out of bounds. - public void WriteText(int index, string text, string defaultText) + public void WriteText(int index, string? text, string defaultText) { BuildPointer(index).WriteText(text ?? defaultText); } diff --git a/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs b/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs index 8c0f19e..6482844 100644 --- a/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs +++ b/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs @@ -61,7 +61,7 @@ { var topDecl = ClassDeclaration(_names.MakeTypeName(def).Identifier) .AddModifiers(Public) - .AddBaseListTypes(SimpleBaseType(_names.Type(true))); + .AddBaseListTypes(SimpleBaseType(_names.Type(Nullability.NonNullable))); if (def.GenericParameters.Count > 0) { @@ -184,6 +184,8 @@ internal string Transform(GenFile file) { + _names.NullableEnable = file.NullableEnable; + NameSyntax topNamespace = GenNames.NamespaceName(file.Namespace) ?? _names.TopNamespace; var ns = NamespaceDeclaration(topNamespace); @@ -204,7 +206,15 @@ UsingDirective(ParseName("Capnp")), UsingDirective(ParseName("Capnp.Rpc")), UsingDirective(ParseName("System")), - UsingDirective(ParseName("System.Collections.Generic")), + UsingDirective(ParseName("System.Collections.Generic"))); + + if (_names.NullableEnable) + { + cu = cu.AddUsings( + UsingDirective(ParseName("System.Diagnostics.CodeAnalysis"))); + } + + cu = cu.AddUsings( UsingDirective(ParseName("System.Threading")), UsingDirective(ParseName("System.Threading.Tasks"))); diff --git a/CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs index 8221147..a94bf8e 100644 --- a/CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs +++ b/CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs @@ -22,7 +22,7 @@ namespace CapnpC.CSharp.Generator.CodeGen { var whichEnum = EnumDeclaration(_names.UnionDiscriminatorEnum.ToString()) .AddModifiers(Public) - .AddBaseListTypes(SimpleBaseType(_names.Type())); + .AddBaseListTypes(SimpleBaseType(_names.Type(Nullability.NonNullable))); var discFields = def.Fields.Where(f => f.DiscValue.HasValue); @@ -52,7 +52,7 @@ namespace CapnpC.CSharp.Generator.CodeGen var decl = EnumDeclaration(def.Name) .WithAttributeLists(MakeTypeIdAttributeLists(def.Id)) .AddModifiers(Public) - .AddBaseListTypes(SimpleBaseType(_names.Type())); + .AddBaseListTypes(SimpleBaseType(_names.Type(Nullability.NonNullable))); foreach (var enumerant in def.Enumerants.OrderBy(e => e.CodeOrder)) { diff --git a/CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs index 9701ff9..7737ae7 100644 --- a/CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs +++ b/CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs @@ -20,7 +20,7 @@ namespace CapnpC.CSharp.Generator.CodeGen MemberDeclarationSyntax MakeUnionField(Field field) { - var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClassNullable); + var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass, Nullability.NullableRefAndValue); switch (field.Type.Tag) { @@ -75,7 +75,8 @@ namespace CapnpC.CSharp.Generator.CodeGen return null; } - var prop = PropertyDeclaration(_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClassNullable), + var prop = PropertyDeclaration( + _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass, Nullability.NullableRef), _names.GetCodeIdentifier(field).Identifier) .AddModifiers(Public).AddAccessorListAccessors( AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) @@ -111,7 +112,7 @@ namespace CapnpC.CSharp.Generator.CodeGen MemberDeclarationSyntax MakeUnionContentField() { return FieldDeclaration( - VariableDeclaration(_names.Type()) + VariableDeclaration(_names.Type(Nullability.NullableRef)) .WithVariables( SingletonSeparatedList( VariableDeclarator(_names.UnionContentField.Identifier)))) @@ -163,7 +164,7 @@ namespace CapnpC.CSharp.Generator.CodeGen case TypeTag.Enum: return MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, - _names.MakeTypeSyntax(value.Type, scope, TypeUsage.NotRelevant), + _names.MakeTypeSyntax(value.Type, scope, TypeUsage.NotRelevant, Nullability.NonNullable), IdentifierName(value.GetEnumerant().Literal)); case TypeTag.F32: @@ -262,7 +263,7 @@ namespace CapnpC.CSharp.Generator.CodeGen value.Decode(); return ObjectCreationExpression( - _names.MakeTypeSyntax(value.Type, scope, TypeUsage.DomainClass)) + _names.MakeTypeSyntax(value.Type, scope, TypeUsage.DomainClass, Nullability.NonNullable)) .WithArgumentList(ArgumentList()) .WithInitializer( InitializerExpression( @@ -282,7 +283,7 @@ namespace CapnpC.CSharp.Generator.CodeGen value.Decode(); return ArrayCreationExpression(ArrayType( - _names.MakeTypeSyntax(value.Type.ElementType, scope, TypeUsage.DomainClassNullable)) + _names.MakeTypeSyntax(value.Type.ElementType, scope, TypeUsage.DomainClass, Nullability.NullableRef)) .WithRankSpecifiers( SingletonList( ArrayRankSpecifier( @@ -344,7 +345,7 @@ namespace CapnpC.CSharp.Generator.CodeGen case TypeTag.Enum: return CastExpression( - _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant), + _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant, Nullability.NonNullable), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))); case TypeTag.F32: @@ -488,6 +489,11 @@ namespace CapnpC.CSharp.Generator.CodeGen } } + ExpressionSyntax ConditionalSuppressNullableWarning(ExpressionSyntax expression, bool suppress) + { + return suppress ? _names.SuppressNullableWarning(expression) : expression; + } + StatementSyntax MakeSerializeMethodFieldAssignment(Field field) { var writerProp = MemberAccessExpression( @@ -519,7 +525,7 @@ namespace CapnpC.CSharp.Generator.CodeGen writerProp, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - _names.GetCodeIdentifier(field).IdentifierName, + _names.SuppressNullableWarning(_names.GetCodeIdentifier(field).IdentifierName), IdentifierName(nameof(Nullable.Value))))); } else @@ -538,10 +544,11 @@ namespace CapnpC.CSharp.Generator.CodeGen InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( + ConditionalSuppressNullableWarning(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.WriterParameter.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName), + field.DiscValue.HasValue), IdentifierName(nameof(Capnp.DynamicSerializerState.SetObject)))) .AddArgumentListArguments( Argument(_names.GetCodeIdentifier(field).IdentifierName))); @@ -568,7 +575,7 @@ namespace CapnpC.CSharp.Generator.CodeGen return ExpressionStatement( MakeComplexSerializeParticle( field.Type, - writerProp, + field.DiscValue.HasValue ? _names.SuppressNullableWarning(writerProp) : writerProp, _names.GetCodeIdentifier(field).IdentifierName)); case TypeTag.Void: @@ -599,20 +606,20 @@ namespace CapnpC.CSharp.Generator.CodeGen ExpressionSyntax MakeInnerStructListConversion(ExpressionSyntax context, TypeSyntax elementType) { - return InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - context, - IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) - .AddArgumentListArguments(Argument( - SimpleLambdaExpression(Parameter(Identifier("_")), - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName(nameof(Capnp.CapnpSerializable)), - GenericName(nameof(Capnp.CapnpSerializable.Create)) - .AddTypeArgumentListArguments(MakeNonNullableType(elementType)))) - .AddArgumentListArguments(Argument(IdentifierName("_")))))); + return ConditionalAccessExpression( + context, + InvocationExpression( + MemberBindingExpression( + IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) + .AddArgumentListArguments(Argument( + SimpleLambdaExpression(Parameter(Identifier("_")), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.CapnpSerializable)), + GenericName(nameof(Capnp.CapnpSerializable.Create)) + .AddTypeArgumentListArguments(MakeNonNullableType(elementType)))) + .AddArgumentListArguments(Argument(IdentifierName("_"))))))); } ExpressionSyntax MakeStructListConversion(ExpressionSyntax context, TypeSyntax elementType, int rank) @@ -624,28 +631,28 @@ namespace CapnpC.CSharp.Generator.CodeGen string lambdaVarName = $"_{rank}"; - return InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - context, - IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) - .AddArgumentListArguments(Argument( - SimpleLambdaExpression( - Parameter(Identifier(lambdaVarName)), - MakeStructListConversion(IdentifierName(lambdaVarName), elementType, rank - 1)))); + return ConditionalAccessExpression( + context, + InvocationExpression( + MemberBindingExpression( + IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) + .AddArgumentListArguments(Argument( + SimpleLambdaExpression( + Parameter(Identifier(lambdaVarName)), + MakeStructListConversion(IdentifierName(lambdaVarName), elementType, rank - 1))))); } ExpressionSyntax MakeAnyListConversion(ExpressionSyntax context) { - return InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - context, - IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) - .AddArgumentListArguments(Argument( - SimpleLambdaExpression( - Parameter(Identifier("_")), - CastExpression(_names.Type(), IdentifierName("_"))))); + return ConditionalAccessExpression( + context, + InvocationExpression( + MemberBindingExpression( + IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) + .AddArgumentListArguments(Argument( + SimpleLambdaExpression( + Parameter(Identifier("_")), + CastExpression(_names.Type(Nullability.NonNullable), IdentifierName("_")))))); } ExpressionSyntax MakeDeserializeMethodRightHandSide(Field field) @@ -665,7 +672,8 @@ namespace CapnpC.CSharp.Generator.CodeGen MakeNonNullableType(_names.MakeTypeSyntax( field.Type, field.DeclaringType, - TypeUsage.DomainClass))))) + TypeUsage.DomainClass, + Nullability.NonNullable))))) .AddArgumentListArguments(Argument(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.ReaderParameter.IdentifierName, @@ -684,7 +692,7 @@ namespace CapnpC.CSharp.Generator.CodeGen SyntaxKind.SimpleMemberAccessExpression, _names.ReaderParameter.IdentifierName, _names.GetCodeIdentifier(field).IdentifierName), - _names.MakeTypeSyntax(elementType, field.DeclaringType, TypeUsage.DomainClassNullable), + _names.MakeTypeSyntax(elementType, field.DeclaringType, TypeUsage.DomainClass, Nullability.NullableRef), rank); case TypeTag.ListPointer: @@ -716,18 +724,6 @@ namespace CapnpC.CSharp.Generator.CodeGen if (unionField.Type.Tag != TypeTag.Void) { - ExpressionSyntax right = _names.GetCodeIdentifier(unionField).IdentifierName; - - var syntax = _names.MakeTypeSyntax(unionField.Type, unionField.DeclaringType, TypeUsage.DomainClassNullable); - - if (syntax is NullableTypeSyntax) - { - right = MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - right, - IdentifierName(nameof(Nullable.Value))); - } - section = section.AddStatements(MakeSerializeMethodFieldAssignment(unionField)); } @@ -849,7 +845,7 @@ namespace CapnpC.CSharp.Generator.CodeGen ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable)))) .AddParameterListParameters( Parameter(_names.AnonymousParameter.Identifier) - .WithType(_names.Type(true))) + .WithType(_names.Type(Nullability.NonNullable))) .AddBodyStatements( ExpressionStatement( InvocationExpression(_names.SerializeMethod.IdentifierName) @@ -933,7 +929,7 @@ namespace CapnpC.CSharp.Generator.CodeGen ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable)))) .AddParameterListParameters( Parameter(_names.AnonymousParameter.Identifier) - .WithType(_names.Type())) + .WithType(_names.Type(Nullability.NonNullable))) .AddBodyStatements(stmts.ToArray()); } @@ -944,7 +940,10 @@ namespace CapnpC.CSharp.Generator.CodeGen if (def.UnionInfo != null) { yield return MakeUnionDiscriminatorField(); - yield return MakeUnionContentField(); + if (def.Fields.Any(f => f.DiscValue.HasValue && f.Type.Tag != TypeTag.Void)) + { + yield return MakeUnionContentField(); + } yield return MakeUnionDiscriminatorProperty(def); } diff --git a/CapnpC.CSharp.Generator/CodeGen/GenNames.cs b/CapnpC.CSharp.Generator/CodeGen/GenNames.cs index 8510c60..1c9e42c 100644 --- a/CapnpC.CSharp.Generator/CodeGen/GenNames.cs +++ b/CapnpC.CSharp.Generator/CodeGen/GenNames.cs @@ -22,11 +22,17 @@ namespace CapnpC.CSharp.Generator.CodeGen { NotRelevant, DomainClass, - DomainClassNullable, Reader, Writer } + enum Nullability + { + NonNullable, + NullableRefAndValue, + NullableRef + } + class GenNames { readonly Dictionary _fieldNameMap = new Dictionary(); @@ -185,7 +191,7 @@ namespace CapnpC.CSharp.Generator.CodeGen TypeSyntax ResolveGenericParameter(GenericParameter p, Model.Type boundType, TypeDefinition def) { var type = boundType.ResolveGenericParameter(p); - return MakeTypeSyntax(type, def, TypeUsage.DomainClass); + return MakeTypeSyntax(type, def, TypeUsage.DomainClass, Nullability.NonNullable); } public SimpleNameSyntax MakeGenericTypeName(TypeDefinition def, Model.Type boundType, NameUsage usage = NameUsage.Default) @@ -289,82 +295,82 @@ namespace CapnpC.CSharp.Generator.CodeGen case TypeTag.AnyPointer: case TypeTag.StructPointer: case TypeTag.ListPointer: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.CapabilityPointer: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.Data: return Type>>(true); + Capnp.ListOfPrimitivesSerializer>>(Nullability.NonNullable); case TypeTag.Enum: return GenericName("ListOfPrimitivesSerializer") - .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); + .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer, Nullability.NonNullable)); case TypeTag.Group: case TypeTag.Struct: return GenericName("ListOfStructsSerializer") - .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); + .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer, Nullability.NonNullable)); case TypeTag.Interface: return GenericName("ListOfCapsSerializer") - .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); + .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer, Nullability.NonNullable)); case TypeTag.List: return GenericName("ListOfPointersSerializer") - .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); + .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer, Nullability.NonNullable)); case TypeTag.Text: - return Type(true); + return Type(Nullability.NonNullable); case TypeTag.Void: - return Type(true); + return Type(Nullability.NonNullable); case TypeTag.Bool: - return Type(true); + return Type(Nullability.NonNullable); case TypeTag.F32: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.F64: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.S8: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.U8: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.S16: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.U16: case TypeTag.AnyEnum: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.S32: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.U32: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.S64: - return Type>(true); + return Type>(Nullability.NonNullable); case TypeTag.U64: - return Type>(true); + return Type>(Nullability.NonNullable); default: throw new NotImplementedException("Unexpected type tag, don't know how to deal with this"); } } - TypeSyntax MaybeNullableValueType(TypeSyntax typeSyntax, TypeUsage usage) + TypeSyntax MaybeNullableValueType(TypeSyntax typeSyntax, Nullability nullability) { - switch (usage) + switch (nullability) { - case TypeUsage.DomainClassNullable: + case Nullability.NullableRefAndValue: return NullableType(typeSyntax); default: @@ -372,76 +378,77 @@ namespace CapnpC.CSharp.Generator.CodeGen } } - public TypeSyntax MakeTypeSyntax(Model.Type type, TypeDefinition scope, TypeUsage usage) + TypeSyntax MaybeNullableRefType(TypeSyntax typeSyntax, Nullability nullability) { - bool nonNullable = usage != TypeUsage.DomainClassNullable; + switch (nullability) + { + case Nullability.NullableRef: + case Nullability.NullableRefAndValue: + return NullableType(typeSyntax); + default: + return typeSyntax; + } + } + + public TypeSyntax MakeTypeSyntax(Model.Type type, TypeDefinition scope, TypeUsage usage, Nullability nullability) + { switch (type.Tag) { case TypeTag.AnyEnum: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.CapabilityPointer: if (type.Parameter != null) - { - return nonNullable ? GetQName(type, scope) : - MakeNullableType(GetQName(type, scope)); - } + return MaybeNullableRefType(GetQName(type, scope), nullability); else - { - return Type(nonNullable); - } + return Type(nullability); case TypeTag.AnyPointer: case TypeTag.StructPointer: switch (usage) { case TypeUsage.Reader: - return Type(); + return Type(Nullability.NonNullable); case TypeUsage.Writer: - return Type(); + return Type(Nullability.NullableRef); + + case TypeUsage.DomainClass when type.Parameter == null: + return Type(nullability); + + case TypeUsage.DomainClass when nullability == Nullability.NonNullable: + return GetQName(type, scope); case TypeUsage.DomainClass: - case TypeUsage.DomainClassNullable: - if (type.Parameter != null) - { - return nonNullable ? - GetQName(type, scope) : - MakeNullableType(GetQName(type, scope)); - } - else - { - return Type(nonNullable); - } + return MakeNullableRefType(GetQName(type, scope)); default: throw new NotImplementedException(); } case TypeTag.Bool: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.Data: switch (usage) { case TypeUsage.Reader: case TypeUsage.DomainClass: - case TypeUsage.DomainClassNullable: - return Type>(nonNullable); + return Type>(nullability); case TypeUsage.Writer: - return Type>(true); + return Type>(nullability); default: throw new NotImplementedException(); } case TypeTag.Enum: - return MaybeNullableValueType(GetQName(type, scope), usage); + return MaybeNullableValueType(GetQName(type, scope), nullability); case TypeTag.Interface: - return GetQName(type, scope); + return MaybeNullableRefType(GetQName(type, scope), nullability); case TypeTag.Struct: case TypeTag.Group: @@ -453,39 +460,38 @@ namespace CapnpC.CSharp.Generator.CodeGen case TypeUsage.Reader: return QualifiedName(GetQName(type, scope), ReaderStruct.IdentifierName); - case TypeUsage.DomainClass: + case TypeUsage.DomainClass when nullability == Nullability.NonNullable: return GetQName(type, scope); - case TypeUsage.DomainClassNullable: - return MakeNullableType(GetQName(type, scope)); + case TypeUsage.DomainClass: + return MakeNullableRefType(GetQName(type, scope)); default: throw new NotImplementedException(); } case TypeTag.F32: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.F64: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.List when type.ElementType.Tag == TypeTag.Void && usage != TypeUsage.Writer: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.List: switch (usage) { case TypeUsage.Writer: - return MakeListSerializerSyntax(type.ElementType, scope); + return MaybeNullableRefType(MakeListSerializerSyntax(type.ElementType, scope), nullability); case TypeUsage.Reader: - return GenericName(Identifier("IReadOnlyList")) - .AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.Reader)); + return MaybeNullableRefType(GenericName(Identifier("IReadOnlyList")) + .AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.Reader, Nullability.NonNullable)), nullability); case TypeUsage.DomainClass: - case TypeUsage.DomainClassNullable: - return GenericName(Identifier("IReadOnlyList")) - .AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, usage)); + return MaybeNullableRefType(GenericName(Identifier("IReadOnlyList")) + .AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.DomainClass, Nullability.NullableRef)), nullability); default: throw new NotImplementedException(); @@ -495,47 +501,49 @@ namespace CapnpC.CSharp.Generator.CodeGen switch (usage) { case TypeUsage.Writer: - return Type(); + return Type(Nullability.NonNullable); case TypeUsage.Reader: - return Type>(); + return Type>(Nullability.NonNullable); + + case TypeUsage.DomainClass when nullability == Nullability.NonNullable: + return GenericName(Identifier("IReadOnlyList")) + .AddTypeArgumentListArguments(Type(Nullability.NullableRef)); case TypeUsage.DomainClass: - return Type>(false); - - case TypeUsage.DomainClassNullable: - return Type>(true); + return MakeNullableRefType(GenericName(Identifier("IReadOnlyList")) + .AddTypeArgumentListArguments(Type(Nullability.NullableRef))); default: throw new NotImplementedException(); } case TypeTag.S16: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.S32: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.S64: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.S8: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.Text: - return Type(nonNullable); + return Type(nullability); case TypeTag.U16: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.U32: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.U64: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.U8: - return MaybeNullableValueType(Type(), usage); + return Type(nullability); case TypeTag.Void: return PredefinedType(Token(SyntaxKind.VoidKeyword)); @@ -648,7 +656,7 @@ namespace CapnpC.CSharp.Generator.CodeGen MakePipeliningSupportExtensionMethodName(path))); } - public TypeSyntax MakeNullableType(TypeSyntax type) + public TypeSyntax MakeNullableRefType(TypeSyntax type) { return NullableEnable ? NullableType(type) : @@ -656,13 +664,19 @@ namespace CapnpC.CSharp.Generator.CodeGen } - public TypeSyntax Type(bool nonNullable = false) + public TypeSyntax Type(Nullability nullability) { - return NullableEnable && !typeof(T).IsValueType && !nonNullable ? + return (NullableEnable && !typeof(T).IsValueType && nullability != Nullability.NonNullable) || + ( typeof(T).IsValueType && nullability == Nullability.NullableRefAndValue) ? NullableType(SyntaxHelpers.NonNullableType()) : SyntaxHelpers.NonNullableType(); } - public ClassOrStructConstraintSyntax MakeNullableClassConstraint() => ClassOrStructConstraint(SyntaxKind.ClassConstraint); + public ExpressionSyntax SuppressNullableWarning(ExpressionSyntax expression) + { + return NullableEnable ? + PostfixUnaryExpression(SyntaxKind.SuppressNullableWarningExpression, expression) : + expression; + } } } diff --git a/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs index d24b9f8..3fe4350 100644 --- a/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs +++ b/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs @@ -27,15 +27,19 @@ namespace CapnpC.CSharp.Generator.CodeGen case 0: return IdentifierName(nameof(Task)); + case 1 when method.Results[0].Type.Tag == TypeTag.Struct: + return GenericName(nameof(Task)).AddTypeArgumentListArguments( + _names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NonNullable)); + case 1: return GenericName(nameof(Task)).AddTypeArgumentListArguments( - _names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClassNullable)); + _names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef)); default: return GenericName(nameof(Task)).AddTypeArgumentListArguments( TupleType(SeparatedList( method.Results.Select( - f => TupleElement(_names.MakeTypeSyntax(f.Type, method.DeclaringInterface, TypeUsage.DomainClassNullable)))))); + f => TupleElement(_names.MakeTypeSyntax(f.Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef)))))); } } @@ -50,14 +54,14 @@ namespace CapnpC.CSharp.Generator.CodeGen if (arg0.Name == null) { list.Add(Parameter(_names.AnonymousParameter.Identifier) - .WithType(_names.MakeTypeSyntax(arg0.Type, method.DeclaringInterface, TypeUsage.DomainClassNullable))); + .WithType(_names.MakeTypeSyntax(arg0.Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef))); } else { foreach (var arg in method.Params) { list.Add(Parameter(Identifier(IdentifierRenamer.ToNonKeyword(arg.Name))) - .WithType(_names.MakeTypeSyntax(arg.Type, method.DeclaringInterface, TypeUsage.DomainClassNullable))); + .WithType(_names.MakeTypeSyntax(arg.Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef))); } } } @@ -85,7 +89,7 @@ namespace CapnpC.CSharp.Generator.CodeGen { yield return TypeParameterConstraintClause( _names.GetGenericTypeParameter(name).IdentifierName) - .AddConstraints(_names.MakeNullableClassConstraint()); + .AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint)); } } @@ -124,7 +128,8 @@ namespace CapnpC.CSharp.Generator.CodeGen ifaceDecl = ifaceDecl.AddBaseListTypes( SimpleBaseType(_names.MakeTypeSyntax( superClass, type, - TypeUsage.NotRelevant))); + TypeUsage.DomainClass, + Nullability.NonNullable))); } } @@ -226,9 +231,9 @@ namespace CapnpC.CSharp.Generator.CodeGen StatementSyntax MakeProxyCreateResult(Method method) { var resultType = method.ResultStruct; - var domainType = _names.MakeTypeSyntax(resultType, method.DeclaringInterface, TypeUsage.DomainClass); + var domainType = _names.MakeTypeSyntax(resultType, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NonNullable); - var createDomain = InvocationExpression( + ExpressionSyntax createDomain = InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nameof(Capnp.CapnpSerializable)), @@ -237,6 +242,13 @@ namespace CapnpC.CSharp.Generator.CodeGen .AddArgumentListArguments( Argument(_names.DeserializerLocal.IdentifierName)); + if (_names.NullableEnable) + { + createDomain = PostfixUnaryExpression( + SyntaxKind.SuppressNullableWarningExpression, + createDomain); + } + return LocalDeclarationStatement( VariableDeclaration( IdentifierName("var")) @@ -262,7 +274,7 @@ namespace CapnpC.CSharp.Generator.CodeGen { yield return TypeParameterConstraintClause( _names.GetGenericTypeParameter(name).IdentifierName) - .AddConstraints(_names.MakeNullableClassConstraint()); + .AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint)); } } @@ -271,7 +283,7 @@ namespace CapnpC.CSharp.Generator.CodeGen var classDecl = ClassDeclaration(_names.MakeTypeName(type, NameUsage.Proxy).Identifier) .AddModifiers(Public) .AddBaseListTypes( - SimpleBaseType(_names.Type(true)), + SimpleBaseType(_names.Type(Nullability.NonNullable)), SimpleBaseType(_names.MakeGenericTypeName(type, NameUsage.Interface))); if (type.GenericParameters.Count > 0) @@ -311,7 +323,7 @@ namespace CapnpC.CSharp.Generator.CodeGen _names.MakeTypeSyntax( method.ParamsStruct, method.ParamsStruct.Definition, - TypeUsage.Writer)))))))))))); + TypeUsage.Writer, Nullability.NonNullable)))))))))))); if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct) { @@ -328,7 +340,8 @@ namespace CapnpC.CSharp.Generator.CodeGen _names.MakeTypeSyntax( method.ParamsStruct, method.ParamsStruct.Definition, - TypeUsage.DomainClass)) + TypeUsage.DomainClass, + Nullability.NonNullable)) .WithArgumentList( ArgumentList()) .WithInitializer( @@ -340,13 +353,12 @@ namespace CapnpC.CSharp.Generator.CodeGen } bodyStmts.Add(ExpressionStatement( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - _names.AnonymousParameter.IdentifierName, - _names.SerializeMethod.IdentifierName)) - .AddArgumentListArguments( - Argument(_names.ParamsLocal.IdentifierName)))); + ConditionalAccessExpression( + _names.AnonymousParameter.IdentifierName, + InvocationExpression( + MemberBindingExpression(_names.SerializeMethod.IdentifierName)) + .AddArgumentListArguments( + Argument(_names.ParamsLocal.IdentifierName))))); var call = InvocationExpression(IdentifierName(nameof(Capnp.Rpc.BareProxy.Call))) .AddArgumentListArguments( @@ -361,7 +373,7 @@ namespace CapnpC.CSharp.Generator.CodeGen SyntaxKind.SimpleMemberAccessExpression, _names.ParamsLocal.IdentifierName, GenericName(nameof(Capnp.SerializerState.Rewrap)) - .AddTypeArgumentListArguments(_names.Type()))) + .AddTypeArgumentListArguments(_names.Type(Nullability.NonNullable)))) .AddArgumentListArguments()), Argument( LiteralExpression(SyntaxKind.FalseLiteralExpression)), @@ -446,7 +458,7 @@ namespace CapnpC.CSharp.Generator.CodeGen GenericName(_names.GetCodeIdentifier(method).ToString()) .AddTypeArgumentListArguments( Enumerable.Repeat( - _names.Type(), + _names.Type(Nullability.NonNullable), method.GenericParameters.Count).ToArray())); } else @@ -502,7 +514,7 @@ namespace CapnpC.CSharp.Generator.CodeGen _names.MakeTypeSyntax( method.ResultStruct, method.ResultStruct.Definition, - TypeUsage.Writer))))))))))); + TypeUsage.Writer, Nullability.NonNullable))))))))))); } @@ -523,7 +535,8 @@ namespace CapnpC.CSharp.Generator.CodeGen _names.MakeTypeSyntax( method.ResultStruct, method.ResultStruct.Definition, - TypeUsage.DomainClass)) + TypeUsage.DomainClass, + Nullability.NonNullable)) .WithInitializer( InitializerExpression(SyntaxKind.ObjectInitializerExpression) .AddExpressions( @@ -575,9 +588,9 @@ namespace CapnpC.CSharp.Generator.CodeGen if (method.Params.Count > 0) { var paramsType = method.ParamsStruct; - var domainType = _names.MakeTypeSyntax(paramsType, method.ParamsStruct.Definition, TypeUsage.DomainClass); + var domainType = _names.MakeTypeSyntax(paramsType, method.ParamsStruct.Definition, TypeUsage.DomainClass, Nullability.NonNullable); - var createDomain = InvocationExpression( + ExpressionSyntax createDomain = InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nameof(Capnp.CapnpSerializable)), @@ -586,6 +599,13 @@ namespace CapnpC.CSharp.Generator.CodeGen .AddArgumentListArguments( Argument(_names.DeserializerLocal.IdentifierName)); + if (_names.NullableEnable) + { + createDomain = PostfixUnaryExpression( + SyntaxKind.SuppressNullableWarningExpression, + createDomain); + } + if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct) { yield return LocalDeclarationStatement( @@ -662,13 +682,13 @@ namespace CapnpC.CSharp.Generator.CodeGen foreach (var method in def.Methods) { var methodDecl = MethodDeclaration( - _names.Type>(), + _names.Type>(Nullability.NonNullable), _names.GetCodeIdentifier(method).Identifier) .AddParameterListParameters( Parameter(_names.DeserializerLocal.Identifier) - .WithType(_names.Type()), + .WithType(_names.Type(Nullability.NonNullable)), Parameter(_names.CancellationTokenParameter.Identifier) - .WithType(_names.Type())) + .WithType(_names.Type(Nullability.NonNullable))) .AddBodyStatements( MakeSkeletonMethodBody(method).ToArray()); @@ -709,7 +729,7 @@ namespace CapnpC.CSharp.Generator.CodeGen .AddArgumentListArguments( MakeSkeletonSetMethodTableArguments(type).ToArray()))), // InterfaceId - PropertyDeclaration(_names.Type(), nameof(Capnp.Rpc.Skeleton.InterfaceId)) + PropertyDeclaration(_names.Type(Nullability.NonNullable), nameof(Capnp.Rpc.Skeleton.InterfaceId)) .AddModifiers(Public, Override) .WithExpressionBody( ArrowExpressionClause( @@ -790,7 +810,7 @@ namespace CapnpC.CSharp.Generator.CodeGen var accessPath = _names.MakeMemberAccessPathFieldName(method, path); var methodName = _names.MakePipeliningSupportExtensionMethodName(path); var capType = path[path.Count - 1].Type; - var capTypeSyntax = _names.MakeTypeSyntax(capType, null, TypeUsage.DomainClass); + var capTypeSyntax = _names.MakeTypeSyntax(capType, null, TypeUsage.DomainClass, Nullability.NonNullable); if (!_existingExtensionMethods.Add((capTypeSyntax.ToString(), methodName.ToString()))) { diff --git a/CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs index 8052dde..8a800ad 100644 --- a/CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs +++ b/CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs @@ -57,7 +57,7 @@ namespace CapnpC.CSharp.Generator.CodeGen ParameterList( SingletonSeparatedList( Parameter(_names.ReaderContextField.Identifier) - .WithType(_names.Type())))) + .WithType(_names.Type(Nullability.NonNullable))))) .WithExpressionBody( ArrowExpressionClause( ObjectCreationExpression(_names.ReaderStruct.IdentifierName) @@ -78,7 +78,7 @@ namespace CapnpC.CSharp.Generator.CodeGen SingletonSeparatedList( Parameter(_names.ContextParameter.Identifier) .WithType( - _names.Type())))) + _names.Type(Nullability.NonNullable))))) .WithExpressionBody( ArrowExpressionClause( ObjectCreationExpression(_names.ReaderStruct.IdentifierName) @@ -90,7 +90,7 @@ namespace CapnpC.CSharp.Generator.CodeGen { yield return FieldDeclaration( VariableDeclaration( - _names.Type()) + _names.Type(Nullability.NonNullable)) .AddVariables(_names.ReaderContextField.VariableDeclarator)) .AddModifiers(Readonly); @@ -100,7 +100,7 @@ namespace CapnpC.CSharp.Generator.CodeGen ParameterList( SingletonSeparatedList( Parameter(_names.ContextParameter.Identifier) - .WithType(_names.Type())))) + .WithType(_names.Type(Nullability.NonNullable))))) .WithBody( Block( SingletonList( @@ -122,7 +122,7 @@ namespace CapnpC.CSharp.Generator.CodeGen { yield return FieldDeclaration( VariableDeclaration( - _names.Type()) + _names.Type(Nullability.NonNullable)) .AddVariables(_names.ReaderContextField.VariableDeclarator)) .AddModifiers(Readonly); @@ -131,7 +131,7 @@ namespace CapnpC.CSharp.Generator.CodeGen .WithParameterList( ParameterList( SingletonSeparatedList(Parameter(_names.GroupReaderContextArg.Identifier) - .WithType(_names.Type())))) + .WithType(_names.Type(Nullability.NonNullable))))) .WithBody( Block( SingletonList( @@ -173,57 +173,7 @@ namespace CapnpC.CSharp.Generator.CodeGen .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); } - static Func MakeCastFunc(TypeSyntax type) => - x => CastExpression(type, x); - - static Func MakeListCastFunc(string castName, Field field) - { - // Insight: List may have complex default values (e.g. [true, false, false, true] as a - // valid default value for a list of bools. This does not yet fit the author's mindset. - - //if (field.DefaultValueIsExplicit) - //{ - // return x => InvocationExpression( - // MemberAccessExpression( - // SyntaxKind.SimpleMemberAccessExpression, - // x, - // IdentifierName(castName)) - // ) - // .AddArgumentListArguments( - // Argument(ValueOf(field.DefaultValue))); - //} - //else - - { - return x => InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - x, - IdentifierName(castName)) - ); - } - } - - static Func MakeListCastFuncWithCons( - string castName, ExpressionSyntax cons) - { - return x => InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - x, - IdentifierName(castName)) - ).AddArgumentListArguments(Argument(cons)); - } - - static Func MakeGenericListCastFunc(string castName, TypeSyntax genericArgument) - { - return x => InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - x, - GenericName(castName).AddTypeArgumentListArguments(genericArgument)) - ); - } + static Func MakeCastFunc(TypeSyntax type) => x => CastExpression(type, x); PropertyDeclarationSyntax MakeReadProperty(TypeSyntax type, string name, SimpleNameSyntax readName, object indexOrBitOffset, ExpressionSyntax secondArg, @@ -261,13 +211,19 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakeReadPrimitiveProperty(Field field, string readName) { - return MakeReadProperty(_names.Type(), _names.GetCodeIdentifier(field).ToString(), readName, field.BitOffset.Value, - ValueOf(field.DefaultValue.ScalarValue), null, field.DiscValue.HasValue); + return MakeReadProperty( + _names.Type(Nullability.NonNullable), + _names.GetCodeIdentifier(field).ToString(), + readName, + field.BitOffset.Value, + ValueOf(field.DefaultValue.ScalarValue), + null, + field.DiscValue.HasValue); } PropertyDeclarationSyntax MakeReadEnumProperty(Field field) { - var typeSyntax = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + var typeSyntax = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable); return MakeReadProperty(typeSyntax, _names.GetCodeIdentifier(field).ToString(), nameof(Capnp.SerializerExtensions.ReadDataUShort), field.BitOffset.Value, @@ -278,9 +234,16 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakeReadTextProperty(Field field) { - return MakeReadProperty(_names.Type(), _names.GetCodeIdentifier(field).ToString(), - nameof(Capnp.DeserializerState.ReadText), (int)field.Offset, - ValueOf(field.DefaultValue.ScalarValue), null, field.DiscValue.HasValue); + bool cantBeNull = !field.DiscValue.HasValue && field.DefaultValue.ScalarValue != null; + + return MakeReadProperty( + _names.Type(cantBeNull ? Nullability.NonNullable : Nullability.NullableRef), + _names.GetCodeIdentifier(field).ToString(), + nameof(Capnp.DeserializerState.ReadText), + (int)field.Offset, + ValueOf(field.DefaultValue.ScalarValue), + null, + field.DiscValue.HasValue); } MemberAccessExpressionSyntax MakeReaderCreator(TypeSyntax qtype) @@ -293,7 +256,7 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakeReadStructProperty(Field field) { - var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable); var creator = MakeReaderCreator(qtype); return MakeReadProperty(qtype, _names.GetCodeIdentifier(field).ToString(), @@ -316,18 +279,16 @@ namespace CapnpC.CSharp.Generator.CodeGen return MakeReaderProperty(type, _names.GetCodeIdentifier(field).ToString(), right, field.DiscValue.HasValue); } - void MakeReadListPropertyImpl(Model.Type elementType, TypeDefinition scope, ExpressionSyntax context, int depth, - out TypeSyntax listType, out ExpressionSyntax impl) + ExpressionSyntax MakeReadListPropertyImpl(Model.Type elementType, TypeDefinition scope, ExpressionSyntax context, int depth) { - var elementTypeSyntax = _names.MakeTypeSyntax(elementType, scope, TypeUsage.Reader); - listType = GenericName("IReadOnlyList").AddTypeArgumentListArguments(elementTypeSyntax); + var elementTypeSyntax = _names.MakeTypeSyntax(elementType, scope, TypeUsage.Reader, Nullability.NonNullable); if (elementType.Tag == TypeTag.Interface || elementType.Tag == TypeTag.CapabilityPointer) { if (depth == 0) { - impl = InvocationExpression(MemberAccessExpression( + return InvocationExpression(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, _names.ReaderContextField.IdentifierName, GenericName(nameof(Capnp.DeserializerState.ReadCapList)) @@ -336,15 +297,13 @@ namespace CapnpC.CSharp.Generator.CodeGen } else { - impl = InvocationExpression( + return InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, context, GenericName(nameof(Capnp.DeserializerState.RequireCapList)) .AddTypeArgumentListArguments(elementTypeSyntax) )); } - - return; } if (depth == 0) @@ -374,56 +333,44 @@ namespace CapnpC.CSharp.Generator.CodeGen case TypeTag.List: { - MakeReadListPropertyImpl( + var innerImpl = MakeReadListPropertyImpl( elementType.ElementType, scope, lambdaArg, - depth + 1, - out var innerListType, - out var innerImpl); + depth + 1); - listType = GenericName("IReadOnlyList").AddTypeArgumentListArguments(innerListType); - - impl = InvocationExpression( + return InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, context, IdentifierName(nameof(Capnp.ListDeserializer.Cast)))) .AddArgumentListArguments( Argument(SimpleLambdaExpression(lambdaParam, innerImpl))); - - return; } case TypeTag.ListPointer: { - listType = _names.Type>(); - context = InvocationExpression(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, context, IdentifierName(nameof(Capnp.DeserializerState.RequireList)) )).AddArgumentListArguments(Argument(lambdaArg)); - impl = InvocationExpression( + return InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, context, IdentifierName(nameof(Capnp.ReadOnlyListExtensions.LazyListSelect)))) .AddArgumentListArguments( Argument(SimpleLambdaExpression(lambdaParam, context))); - - return; } case TypeTag.Struct: { - impl = InvocationExpression( + return InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, context, IdentifierName(nameof(Capnp.ListDeserializer.Cast)))) .AddArgumentListArguments( Argument(MakeReaderCreator(elementTypeSyntax))); - - return; } case TypeTag.Enum: @@ -432,32 +379,26 @@ namespace CapnpC.CSharp.Generator.CodeGen lambdaParam, CastExpression(elementTypeSyntax, lambdaArg)); - impl = InvocationExpression( + return InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, context, IdentifierName(nameof(Capnp.ListDeserializer.CastEnums)))) .AddArgumentListArguments( Argument(cons)); - - return; } case TypeTag.AnyPointer: case TypeTag.StructPointer: { - listType = _names.Type>(); - impl = context; - return; + return context; } case TypeTag.Void: { - listType = _names.Type(); - impl = MemberAccessExpression( + return MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, context, IdentifierName(nameof(Capnp.ListDeserializer.Count))); - return; } case TypeTag.Data: @@ -517,7 +458,7 @@ namespace CapnpC.CSharp.Generator.CodeGen throw new NotImplementedException("Unexpected type tag, don't know how to deal with this"); } - impl = InvocationExpression(MemberAccessExpression( + return InvocationExpression(MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, context, IdentifierName(castFuncName))); @@ -527,13 +468,18 @@ namespace CapnpC.CSharp.Generator.CodeGen { var elementType = field.Type.ElementType; var context = ValueOf((int)field.Offset); - MakeReadListPropertyImpl(elementType, field.DeclaringType, context, 0, out var listType, out var impl); + var impl = MakeReadListPropertyImpl(elementType, field.DeclaringType, context, 0); + var listType = _names.MakeTypeSyntax( + field.Type, + field.DeclaringType, + TypeUsage.Reader, + field.DiscValue.HasValue ? Nullability.NullableRef : Nullability.NonNullable); return MakeReaderProperty(listType, _names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue); } PropertyDeclarationSyntax MakeReadAnyListProperty(Field field) { - var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable); return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), nameof(Capnp.DeserializerState.ReadList), @@ -542,18 +488,19 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakeReadCapProperty(Field field) { - var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + var nullableType = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NullableRef); + var nonNullableType = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable); var readName = GenericName(nameof(Capnp.DeserializerState.ReadCap)) - .AddTypeArgumentListArguments(type); + .AddTypeArgumentListArguments(nonNullableType); - return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), + return MakeReadProperty(nullableType, _names.GetCodeIdentifier(field).ToString(), readName, (int)field.Offset, null, null, field.DiscValue.HasValue); } PropertyDeclarationSyntax MakeReadAnyCapProperty(Field field) { - var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, Nullability.NonNullable); return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), nameof(Capnp.DeserializerState.ReadCap), @@ -573,7 +520,8 @@ namespace CapnpC.CSharp.Generator.CodeGen IdentifierName(nameof(Capnp.ListDeserializer.CastByte)))); return MakeReaderProperty( - _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader), + _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader, + field.DiscValue.HasValue ? Nullability.NullableRefAndValue : Nullability.NonNullable), _names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue); } diff --git a/CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs index ffbd93b..7423adb 100644 --- a/CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs +++ b/CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs @@ -115,12 +115,21 @@ namespace CapnpC.CSharp.Generator.CodeGen .AddArgumentListArguments( Argument(ValueOf(index))); - ExpressionSyntax MakeLinkSyntax(object index) => - InvocationExpression( + ExpressionSyntax MakeLinkSyntax(object index, bool suppressNullableWarning) + { + ExpressionSyntax value = IdentifierName("value"); + + if (suppressNullableWarning) + { + value = _names.SuppressNullableWarning(value); + } + + return InvocationExpression( IdentifierName(SerializerStateWorder.LinkName)) .AddArgumentListArguments( Argument(ValueOf(index)), - Argument(IdentifierName("value"))); + Argument(value)); + } ExpressionSyntax MakeLinkObjectSyntax(object index) => InvocationExpression( @@ -129,22 +138,46 @@ namespace CapnpC.CSharp.Generator.CodeGen Argument(ValueOf(index)), Argument(IdentifierName("value"))); + PropertyDeclarationSyntax MakeWriterRefTypeProperty( + TypeSyntax type, + string name, + ExpressionSyntax getter, + ExpressionSyntax setter, + bool cast, + bool cond) + { + if (cond) + { + type = _names.MakeNullableRefType(type); + } + + var prop = MakeWriterProperty(type, name, getter, setter, cast, cond); + + if (cond && _names.NullableEnable) + { + prop = prop.AddAttributeLists( + AttributeList( + SingletonSeparatedList( + Attribute( + IdentifierName("DisallowNull"))))); + } + return prop; + } + PropertyDeclarationSyntax MakePointerProperty(TypeSyntax type, string name, object index, bool cast, bool cond) { ExpressionSyntax getter = MakePointerSyntax(type, index); - ExpressionSyntax setter = MakeLinkSyntax(index); + ExpressionSyntax setter = MakeLinkSyntax(index, cond); - return MakeWriterProperty(type, name, getter, setter, cast, cond); + return MakeWriterRefTypeProperty(type, name, getter, setter, cast, cond); } - PropertyDeclarationSyntax MakePointerAsStructProperty( - TypeSyntax type, string name, object index, - bool cast, bool cond) + PropertyDeclarationSyntax MakePointerAsStructProperty(TypeSyntax type, string name, object index, bool cast, bool cond) { ExpressionSyntax getter = MakeTypedPointerSyntax(index, type); - ExpressionSyntax setter = MakeLinkSyntax(index); + ExpressionSyntax setter = MakeLinkSyntax(index, cond); - return MakeWriterProperty(type, name, getter, setter, cast, cond); + return MakeWriterRefTypeProperty(type, name, getter, setter, cast, cond); } PropertyDeclarationSyntax MakeProperty( @@ -195,7 +228,10 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakePrimitiveProperty(Field field, string readName) { - return MakeProperty(_names.Type(), null, _names.GetCodeIdentifier(field).ToString(), + return MakeProperty( + _names.Type(Nullability.NonNullable), + null, + _names.GetCodeIdentifier(field).ToString(), readName, nameof(Capnp.SerializerExtensions.WriteData), field.BitOffset.Value, @@ -207,7 +243,9 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakeEnumProperty(Field field, string readName) { - return MakeProperty(_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant), _names.Type(), + return MakeProperty( + _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant, Nullability.NonNullable), + _names.Type(Nullability.NonNullable), _names.GetCodeIdentifier(field).ToString(), readName, nameof(Capnp.SerializerExtensions.WriteData), @@ -220,7 +258,9 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakeTextProperty(Field field) { - return MakeProperty(_names.Type(), null, + return MakeProperty( + _names.Type(Nullability.NullableRef), + null, _names.GetCodeIdentifier(field).ToString(), nameof(Capnp.SerializerState.ReadText), nameof(Capnp.SerializerState.WriteText), @@ -233,7 +273,7 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakeStructProperty(Field field) { - var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer); + var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer, Nullability.NonNullable); return MakePointerAsStructProperty(qtype, _names.GetCodeIdentifier(field).ToString(), (int)field.Offset, false, field.DiscValue.HasValue); @@ -241,7 +281,7 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakeGroupProperty(Field field) { - var type = QualifiedName( + TypeSyntax type = QualifiedName( _names.MakeTypeName(field.Type.Definition).IdentifierName, _names.WriterStruct.IdentifierName); @@ -249,12 +289,17 @@ namespace CapnpC.CSharp.Generator.CodeGen GenericName(nameof(Capnp.SerializerState.Rewrap)) .AddTypeArgumentListArguments(type)); + if (field.DiscValue.HasValue) + { + type = _names.MakeNullableRefType(type); + } + return MakeWriterProperty(type, _names.GetCodeIdentifier(field).ToString(), getter, null, false, field.DiscValue.HasValue); } PropertyDeclarationSyntax MakeListProperty(Field field) { - var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer); + var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer, Nullability.NonNullable); return MakePointerProperty(qtype, _names.GetCodeIdentifier(field).ToString(), (int)field.Offset, false, field.DiscValue.HasValue); @@ -269,20 +314,21 @@ namespace CapnpC.CSharp.Generator.CodeGen PropertyDeclarationSyntax MakeCapProperty(Field field) { - var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer); + var nonNullableType = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer, Nullability.NonNullable); + var nullableType = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer, Nullability.NullableRef); int index = (int)field.Offset; string name = _names.GetCodeIdentifier(field).ToString(); - ExpressionSyntax getter = MakeReadCapSyntax(type, index); + ExpressionSyntax getter = MakeReadCapSyntax(nonNullableType, index); ExpressionSyntax setter = MakeLinkObjectSyntax(index); - return MakeWriterProperty(type, name, getter, setter, false, field.DiscValue.HasValue); + return MakeWriterProperty(nullableType, name, getter, setter, false, field.DiscValue.HasValue); } PropertyDeclarationSyntax MakeWriterUnionSelector(TypeDefinition def) { return MakeProperty( _names.UnionDiscriminatorEnum.IdentifierName, - _names.Type(), + _names.Type(Nullability.NonNullable), _names.UnionDiscriminatorProp.ToString(), nameof(Capnp.SerializerExtensions.ReadDataUShort), nameof(Capnp.SerializerExtensions.WriteData), diff --git a/MsBuildGenerationTest/Program.cs b/MsBuildGenerationTest/Program.cs index bbbb50b..61811e8 100644 --- a/MsBuildGenerationTest/Program.cs +++ b/MsBuildGenerationTest/Program.cs @@ -9,13 +9,24 @@ namespace MsBuildGenerationTest // Instantiate some generated classes ensures that they are really present. // Note that this code is not supposed to test runtime behavior, we have plenty of other test cases for that purpose. + void use(object x) + { + } + var vatId = new Capnp.Rpc.Twoparty.VatId(); + use(vatId); var msg = new Capnp.Rpc.Message(); + use(msg); var node = new Capnp.Schema.Node(); + use(node); var x = Capnproto_test.Capnp.Test.TestEnum.garply; + use(x); var imp = new CapnpGen.TestImport(); + use(imp); var imp2 = new CapnpGen.TestImport2(); + use(imp2); var book = new CapnpGen.AddressBook(); + use(book); } } } diff --git a/MsBuildGenerationTest/capnp/rpc.capnp b/MsBuildGenerationTest/capnp/rpc.capnp index 86e86ea..2e24b62 100644 --- a/MsBuildGenerationTest/capnp/rpc.capnp +++ b/MsBuildGenerationTest/capnp/rpc.capnp @@ -110,7 +110,7 @@ # it for free. using Cxx = import "/capnp/c++.capnp"; -$Cxx.namespace("capnp::rpc"); +$Cxx.namespace("test::rpc"); # ======================================================================================== # The Four Tables