From 71c94d3fb435135135932faf64e9c2f70743f816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 21 Dec 2023 16:46:13 +0100 Subject: [PATCH] =?UTF-8?q?Unconditionally=20generate=20C#=C2=A0classes=20?= =?UTF-8?q?with=20nullable=20reference=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #3944 --- .../Writers/CSharp/CSharpConventionService.cs | 19 +--------------- .../CSharp/CodeClassDeclarationWriter.cs | 2 ++ .../Writers/CSharp/CodeMethodWriter.cs | 12 ++++------ .../Writers/CSharp/CodePropertyWriter.cs | 12 +--------- .../Writers/CSharp/CodeMethodWriterTests.cs | 22 +++++++++---------- .../Writers/CSharp/CodePropertyWriterTests.cs | 4 ++-- 6 files changed, 21 insertions(+), 50 deletions(-) diff --git a/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs b/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs index 6c71029cf6..309d19d5c9 100644 --- a/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs +++ b/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -19,23 +19,6 @@ public class CSharpConventionService : CommonLanguageConventionService public static string NullableMarkerAsString => "?"; public override string ParseNodeInterfaceName => "IParseNode"; - public static void WriteNullableOpening(LanguageWriter writer) - { - ArgumentNullException.ThrowIfNull(writer); - writer.WriteLine($"#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER", false); - writer.WriteLine($"#nullable enable", false); - } - public static void WriteNullableMiddle(LanguageWriter writer) - { - ArgumentNullException.ThrowIfNull(writer); - writer.WriteLine($"#nullable restore", false); - writer.WriteLine("#else", false); - } - public static void WriteNullableClosing(LanguageWriter writer) - { - ArgumentNullException.ThrowIfNull(writer); - writer.WriteLine("#endif", false); - } private const string ReferenceTypePrefix = ""; #pragma warning disable S1006 // Method overrides should not change parameter defaults diff --git a/src/Kiota.Builder/Writers/CSharp/CodeClassDeclarationWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeClassDeclarationWriter.cs index fbd487b882..1876697983 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeClassDeclarationWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeClassDeclarationWriter.cs @@ -7,6 +7,7 @@ namespace Kiota.Builder.Writers.CSharp; public class CodeClassDeclarationWriter : BaseElementWriter { public static string AutoGenerationHeader => "// "; + public static string NullableHeader => "#nullable enable"; public CodeClassDeclarationWriter(CSharpConventionService conventionService) : base(conventionService) { } public override void WriteCodeElement(ClassDeclaration codeElement, LanguageWriter writer) { @@ -17,6 +18,7 @@ public override void WriteCodeElement(ClassDeclaration codeElement, LanguageWrit if (codeElement.Parent?.Parent is CodeNamespace) { writer.WriteLine(AutoGenerationHeader); + writer.WriteLine(NullableHeader); codeElement.Usings .Where(x => (x.Declaration?.IsExternal ?? true) || !x.Declaration.Name.Equals(codeElement.Name, StringComparison.OrdinalIgnoreCase)) // needed for circular requests patterns like message folder .Select(static x => x.Declaration?.IsExternal ?? false ? diff --git a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs index 08a2e53183..df89573da6 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs @@ -629,16 +629,12 @@ private void WriteMethodPrototype(CodeMethod code, CodeClass parentClass, Langua GetParameterSignatureWithNullableRefType(p, code) : conventions.GetParameterSignature(p, code)) .ToList()); - CSharpConventionService.WriteNullableOpening(writer); writer.WriteLine($"{conventions.GetAccessModifier(code.Access)} {staticModifier}{hideModifier}{completeReturnTypeWithNullable}{methodName}({nullableParameters}){baseSuffix} {{"); - CSharpConventionService.WriteNullableMiddle(writer); } - - writer.WriteLine($"{conventions.GetAccessModifier(code.Access)} {staticModifier}{hideModifier}{completeReturnType}{methodName}({parameters}){baseSuffix} {{"); - - if (includeNullableReferenceType) - CSharpConventionService.WriteNullableClosing(writer); - + else + { + writer.WriteLine($"{conventions.GetAccessModifier(code.Access)} {staticModifier}{hideModifier}{completeReturnType}{methodName}({parameters}){baseSuffix} {{"); + } } private string GetParameterSignatureWithNullableRefType(CodeParameter parameter, CodeElement targetElement) diff --git a/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs index e4b9efffe7..cb38f6b1c2 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs @@ -18,17 +18,7 @@ public override void WriteCodeElement(CodeProperty codeElement, LanguageWriter w CodePropertyKind.QueryParameter);// Other property types are appropriately constructor initialized conventions.WriteShortDescription(codeElement, writer); conventions.WriteDeprecationAttribute(codeElement, writer); - if (isNullableReferenceType) - { - CSharpConventionService.WriteNullableOpening(writer); - WritePropertyInternal(codeElement, writer, $"{propertyType}?"); - CSharpConventionService.WriteNullableMiddle(writer); - } - - WritePropertyInternal(codeElement, writer, propertyType);// Always write the normal way - - if (isNullableReferenceType) - CSharpConventionService.WriteNullableClosing(writer); + WritePropertyInternal(codeElement, writer, isNullableReferenceType ? $"{propertyType}?" : propertyType); } private void WritePropertyInternal(CodeProperty codeElement, LanguageWriter writer, string propertyType) diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs index 820e7d2a5d..406fe74113 100644 --- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs @@ -479,7 +479,7 @@ public void WritesRequestExecutorBody() Assert.Contains(AsyncKeyword, result); Assert.Contains("await", result); Assert.Contains("cancellationToken", result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesRequestGeneratorBodyForMultipart() @@ -493,7 +493,7 @@ public void WritesRequestGeneratorBodyForMultipart() writer.Write(method); var result = tw.ToString(); Assert.Contains("SetContentFromParsable", result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesRequestExecutorBodyForCollection() @@ -518,7 +518,7 @@ public void WritesRequestExecutorBodyForCollection() Assert.Contains("SendCollectionAsync", result); Assert.Contains("return collectionResult?.ToList()", result); Assert.Contains($"{ReturnTypeName}.CreateFromDiscriminatorValue", result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void DoesntCreateDictionaryOnEmptyErrorMapping() @@ -531,7 +531,7 @@ public void DoesntCreateDictionaryOnEmptyErrorMapping() var result = tw.ToString(); Assert.DoesNotContain("var errorMapping = new Dictionary>", result); Assert.Contains("default", result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesModelFactoryBodyForUnionModels() @@ -917,7 +917,7 @@ public void WritesRequestExecutorBodyForCollections() var result = tw.ToString(); Assert.Contains("SendCollectionAsync", result); Assert.Contains("cancellationToken", result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesRequestGeneratorBodyForNullableScalar() @@ -935,7 +935,7 @@ public void WritesRequestGeneratorBodyForNullableScalar() Assert.Contains("requestInfo.Configure(config)", result); Assert.Contains("SetContentFromScalar", result); Assert.Contains("return requestInfo;", result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesRequestGeneratorBodyForScalar() @@ -956,7 +956,7 @@ public void WritesRequestGeneratorBodyForScalar() Assert.Contains("return requestInfo;", result); Assert.Contains("async Task", result);//verify we only have one nullable marker Assert.DoesNotContain("async Task", result);//verify we only have one nullable marker - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesRequestGeneratorBodyForParsable() @@ -974,7 +974,7 @@ public void WritesRequestGeneratorBodyForParsable() Assert.Contains("requestInfo.Configure(config)", result); Assert.Contains("SetContentFromParsable", result); Assert.Contains("return requestInfo;", result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesRequestGeneratorBodyWhenUrlTemplateIsOverrode() @@ -1010,7 +1010,7 @@ public void WritesRequestGeneratorBodyForScalarCollection() writer.Write(method); var result = tw.ToString(); Assert.Contains("SetContentFromScalarCollection", result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesRequestGeneratorBodyKnownRequestBodyType() @@ -1030,7 +1030,7 @@ public void WritesRequestGeneratorBodyKnownRequestBodyType() var result = tw.ToString(); Assert.Contains("SetStreamContent", result, StringComparison.OrdinalIgnoreCase); Assert.Contains("application/json", result, StringComparison.OrdinalIgnoreCase); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesRequestGeneratorBodyUnknownRequestBodyType() @@ -1060,7 +1060,7 @@ public void WritesRequestGeneratorBodyUnknownRequestBodyType() Assert.Contains("SetStreamContent", result, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("application/json", result, StringComparison.OrdinalIgnoreCase); Assert.Contains(", requestContentType", result, StringComparison.OrdinalIgnoreCase); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result); } [Fact] public void WritesInheritedDeSerializerBody() diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs index 27516fbfd2..da941dfa90 100644 --- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs @@ -84,7 +84,7 @@ public void WritesCustomProperty() property.Kind = CodePropertyKind.Custom; writer.Write(property); var result = tw.ToString(); - Assert.Contains($"{TypeName} {PropertyName}", result); + Assert.Contains($"{TypeName}? {PropertyName}", result); Assert.Contains("get; set;", result); } [Fact] @@ -103,7 +103,7 @@ public void MapsCustomPropertiesToBackingStore() property.Kind = CodePropertyKind.Custom; writer.Write(property); var result = tw.ToString(); - Assert.Contains("get { return BackingStore?.Get(\"propertyName\"); }", result); + Assert.Contains("get { return BackingStore?.Get(\"propertyName\"); }", result); Assert.Contains("set { BackingStore?.Set(\"propertyName\", value);", result); } [Fact]