diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs index 80a857b9..7bf0d6cf 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs @@ -144,6 +144,78 @@ private void ResolveAnonymousCursorName(NamedDecl namedDecl, ReadOnlySpan } } + private static string GetNamespaceQualifiedNativeTypeName(Type type, string nativeTypeName) + { + // Peel pointer/reference/array layers so a stripped namespace is restored even for + // `Ns::Point *` and similar, where clang only ever drops the leading `Namespace::`. + + var namedType = type; + + while (true) + { + if (namedType is PointerType pointerType) + { + namedType = pointerType.PointeeType; + } + else if (namedType is ReferenceType referenceType) + { + namedType = referenceType.PointeeType; + } + else if (namedType is ArrayType arrayType) + { + namedType = arrayType.ElementType; + } + else if (namedType is AttributedType attributedType) + { + namedType = attributedType.ModifiedType; + } + else + { + break; + } + } + + Decl? decl = namedType switch { + TagType tagType => tagType.Decl, + TypedefType typedefType => typedefType.Decl, + _ => null, + }; + + // A synthesized name for an anonymous decl carries no source namespace to restore. + if (decl is null || decl.Handle.IsAnonymous) + { + return nativeTypeName; + } + + return GetNamespaceQualifiedNativeTypeName(decl, nativeTypeName); + } + + private static string GetNamespaceQualifiedNativeTypeName(Decl decl, string nativeTypeName) + { + // clang 22's type printer omits the enclosing C++ namespace from a reference when a + // matching `using namespace` is in scope, where-as older releases always spelled it. + // Rebuild the dropped `Namespace::` prefix from the decl so the NativeTypeName keeps the + // fully qualified source spelling (e.g. `Gdiplus::Status`) and stays stable across versions. + + var qualifierBuilder = new StringBuilder(); + + for (var declContext = decl.DeclContext; declContext is Decl parentDecl; declContext = parentDecl.DeclContext) + { + if (parentDecl is NamespaceDecl namespaceDecl && !string.IsNullOrEmpty(namespaceDecl.Name)) + { + _ = qualifierBuilder.Insert(0, "::").Insert(0, namespaceDecl.Name); + } + } + + if (qualifierBuilder.Length == 0) + { + return nativeTypeName; + } + + var qualifier = qualifierBuilder.ToString(); + return nativeTypeName.StartsWith(qualifier, StringComparison.Ordinal) ? nativeTypeName : qualifier + nativeTypeName; + } + private string GetCursorQualifiedName(NamedDecl namedDecl, bool truncateParameters = false) { if (!_cursorQualifiedNames.TryGetValue((namedDecl, truncateParameters), out var qualifiedName)) @@ -563,40 +635,6 @@ private string GetRemappedCursorName(NamedDecl namedDecl, out string nativeTypeN return remappedName; } - private string ApplyTagTypeNameOverrides(TagType tagType, string leafName) - { - // Keep a `--remap-type` override or a de-clashed nested record name (see - // GetRemappedCursorName) consistent between the type declaration and any reference to it, - // so both resolve to the same C# type. - - if (_config._remappedTypeNames.Count != 0) - { - var remappedTypeNamesLookup = _config._remappedTypeNames.GetAlternateLookup>(); - - if (remappedTypeNamesLookup.TryGetValue(leafName, out var remappedTypeName)) - { - return remappedTypeName; - } - } - - // A `--remap-type` takes precedence over prefix stripping, matching the declaration side. - leafName = StripTypePrefix(leafName); - - if ((tagType.Decl is RecordDecl recordDecl) && TryDeclashRecordName(recordDecl, leafName, out var declashedName)) - { - leafName = declashedName; - } - - // Mirror the declaration side (see GetRemappedCursorName): a lowercase-only type name is - // `@`-escaped so the reference resolves to the same escaped C# type and compiles without CS8981. - if (IsLowercaseAsciiOnly(leafName)) - { - leafName = $"@{leafName}"; - } - - return leafName; - } - private bool TryDeclashRecordName(RecordDecl recordDecl, string name, out string declashedName) { declashedName = name; diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Predicates.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Predicates.cs index 327b18c5..7f734a3f 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Predicates.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Predicates.cs @@ -907,6 +907,12 @@ private static bool IsType(Cursor? cursor, Type type, [MaybeNullWhen(false)] return IsType(cursor, usingType.Desugar, out value); } } + else if (type.IsSugared && (type.Desugar != type)) + { + // A sugar type class we don't specifically handle (e.g. clang 22's + // PredefinedSugarType for `size_t`) still desugars to its underlying type. + return IsType(cursor, type.Desugar, out value); + } value = default; return false; diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs index 5338a41f..0a1a6f6e 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs @@ -367,33 +367,22 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type result.typeName = GetAnonymousName(tagType.Decl, tagType.KindSpelling); result.nativeTypeName = result.typeName; } - else if (tagType.Handle.IsConstQualified) + else if (tagType.UnqualifiedType != tagType) { + // The type carries a local qualifier (const, volatile, or an MS extension + // such as `__unaligned`) that clang folds into the spelling. Resolve the + // decl's own type so the qualifier keyword doesn't leak into the name. result.typeName = GetTypeName(cursor, context, rootType, tagType.Decl.TypeForDecl, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else { - // The default name should be correct for C++, but C may have a prefix we need to strip + // Resolve the name through the decl so a reference matches its declaration exactly, + // including a `--remap` keyed by the fully qualified name (e.g. `Gdiplus.PointF`). + // clang's type printer may or may not spell the enclosing namespace depending on a + // `using namespace`, so keying off the decl's qualified name rather than the printed + // spelling keeps declarations and references in agreement. - if (result.typeName.StartsWith("enum ", StringComparison.Ordinal)) - { - result.typeName = result.typeName[5..]; - } - else if (result.typeName.StartsWith("struct ", StringComparison.Ordinal)) - { - result.typeName = result.typeName[7..]; - } - else if (result.typeName.StartsWith("union ", StringComparison.Ordinal)) - { - result.typeName = result.typeName[6..]; - } - } - - if (result.typeName.Contains("::", StringComparison.Ordinal)) - { - result.typeName = result.typeName.Split(s_doubleColonSeparator, StringSplitOptions.RemoveEmptyEntries).Last(); - result.typeName = GetRemappedName(result.typeName, cursor, tryRemapOperatorName: false, out _, skipUsing: true); - result.typeName = ApplyTagTypeNameOverrides(tagType, result.typeName); + result.typeName = GetRemappedCursorName(tagType.Decl, out _, skipUsing: true); // A nested type needs to be qualified by its containing type(s) so it resolves // when referenced from another scope (e.g. `A::Inner` -> `A.Inner`). Namespaces @@ -409,10 +398,6 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type result.typeName = qualificationBuilder.Append(result.typeName).ToString(); } - else - { - result.typeName = ApplyTagTypeNameOverrides(tagType, result.typeName); - } } else if (type is TemplateSpecializationType templateSpecializationType) { @@ -550,6 +535,12 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type { result.typeName = EscapeName(GetRemappedCursorName(objCInterfaceType.Decl)); } + else if (type.IsSugared && (type.Desugar != type)) + { + // A sugar type class we don't specifically handle (e.g. clang 22's + // PredefinedSugarType for `size_t`) still desugars to its underlying type. + result.typeName = GetTypeName(cursor, context, rootType, type.Desugar, ignoreTransparentStructsWhereRequired, isTemplate, out _); + } else { AddDiagnostic(DiagnosticLevel.Warning, $"Unsupported type: '{type.TypeClass}'. Falling back '{result.typeName}'.", cursor); @@ -558,6 +549,12 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type Debug.Assert(!string.IsNullOrWhiteSpace(result.typeName)); Debug.Assert(!string.IsNullOrWhiteSpace(result.nativeTypeName)); + // clang 22's type printer omits the enclosing C++ namespace from a reference when the + // namespace is in scope (a `using namespace` or a reference from within the namespace), + // where-as older releases always spelled it. Restore the dropped `Namespace::` prefix from + // the referenced decl so the NativeTypeName keeps the fully qualified, version-stable spelling. + result.nativeTypeName = GetNamespaceQualifiedNativeTypeName(type, result.nativeTypeName); + if (IsNativeTypeNameEquivalent(result.nativeTypeName, result.typeName)) { result.nativeTypeName = string.Empty; diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index d9d1140a..4e2ab4b3 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -51,7 +51,6 @@ public sealed partial class PInvokeGenerator : IDisposable private static readonly Encoding s_defaultStreamWriterEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); private static readonly SearchValues s_lowercaseAsciiLetters = SearchValues.Create("abcdefghijklmnopqrstuvwxyz"); - private static readonly string[] s_doubleColonSeparator = ["::"]; private static readonly char[] s_doubleQuoteSeparator = ['"']; private static readonly SearchValues s_qualifiedNameSeparatorChars = SearchValues.Create(".:"); diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/NamespaceQualifiedNativeTypeNameIsPreservedTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/NamespaceQualifiedNativeTypeNameIsPreservedTest.CSharp.cs new file mode 100644 index 00000000..52febb1b --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/NamespaceQualifiedNativeTypeNameIsPreservedTest.CSharp.cs @@ -0,0 +1,32 @@ +namespace ClangSharp.Test +{ + public partial struct Point + { + [NativeTypeName("Ns::Real")] + public float X; + + [NativeTypeName("Ns::Real")] + public float Y; + } + + public enum Status + { + Err = -1, + Ok = 0, + } + + public unsafe partial struct Holder + { + [NativeTypeName("Ns::Point")] + public Point point; + + [NativeTypeName("Ns::Point *")] + public Point* pointPtr; + + [NativeTypeName("Ns::Real")] + public float r; + + [NativeTypeName("Ns::Status")] + public Status status; + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/NamespaceQualifiedNativeTypeNameIsPreservedTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/NamespaceQualifiedNativeTypeNameIsPreservedTest.Xml.xml new file mode 100644 index 00000000..74382d9a --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceNativeTypeName/NamespaceQualifiedNativeTypeNameIsPreservedTest.Xml.xml @@ -0,0 +1,42 @@ + + + + + + float + + + float + + + + int + + int + + -1 + + + + int + + 0 + + + + + + Point + + + Point* + + + float + + + Status + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceRemap/NamespaceQualifiedRemapAppliesAtUseSiteTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceRemap/NamespaceQualifiedRemapAppliesAtUseSiteTest.CSharp.cs new file mode 100644 index 00000000..a7241a72 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceRemap/NamespaceQualifiedRemapAppliesAtUseSiteTest.CSharp.cs @@ -0,0 +1,27 @@ +namespace ClangSharp.Test +{ + public partial struct NsPoint + { + public int X; + + public int Y; + } + + public enum NsStatus + { + Err = -1, + Ok = 0, + } + + public unsafe partial struct Holder + { + [NativeTypeName("Ns::Point")] + public NsPoint point; + + [NativeTypeName("Ns::Point *")] + public NsPoint* pointPtr; + + [NativeTypeName("Ns::Status")] + public NsStatus status; + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceRemap/NamespaceQualifiedRemapAppliesAtUseSiteTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceRemap/NamespaceQualifiedRemapAppliesAtUseSiteTest.Xml.xml new file mode 100644 index 00000000..2302a417 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/NamespaceRemap/NamespaceQualifiedRemapAppliesAtUseSiteTest.Xml.xml @@ -0,0 +1,39 @@ + + + + + + int + + + int + + + + int + + int + + -1 + + + + int + + 0 + + + + + + NsPoint + + + NsPoint* + + + NsStatus + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/SizeTMacroBinding/SizeTGenerateUnmanagedConstantsMacroTest.CSharp.Latest.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/SizeTMacroBinding/SizeTGenerateUnmanagedConstantsMacroTest.CSharp.Latest.Windows.cs new file mode 100644 index 00000000..be9884b7 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/SizeTMacroBinding/SizeTGenerateUnmanagedConstantsMacroTest.CSharp.Latest.Windows.cs @@ -0,0 +1,8 @@ +namespace ClangSharp.Test +{ + public static partial class Methods + { + [NativeTypeName("#define MY_INT_SIZE sizeof(int)")] + public const ulong MY_INT_SIZE = 4; + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/SizeTMacroBinding/SizeTMacroTest.CSharp.Latest.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/SizeTMacroBinding/SizeTMacroTest.CSharp.Latest.Windows.cs new file mode 100644 index 00000000..be9884b7 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/SizeTMacroBinding/SizeTMacroTest.CSharp.Latest.Windows.cs @@ -0,0 +1,8 @@ +namespace ClangSharp.Test +{ + public static partial class Methods + { + [NativeTypeName("#define MY_INT_SIZE sizeof(int)")] + public const ulong MY_INT_SIZE = 4; + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/UnalignedTypeBinding/UnalignedRecordPointerParameterTest.CSharp.Latest.Windows.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/UnalignedTypeBinding/UnalignedRecordPointerParameterTest.CSharp.Latest.Windows.cs new file mode 100644 index 00000000..d42374c0 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/UnalignedTypeBinding/UnalignedRecordPointerParameterTest.CSharp.Latest.Windows.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public partial struct tagMETARECORD + { + public int rdSize; + } + + public static unsafe partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?PlayMetaFileRecord@@YAXPEFAUtagMETARECORD@@@Z", ExactSpelling = true)] + public static extern void PlayMetaFileRecord([NativeTypeName("LPMETARECORD")] tagMETARECORD* lpMR); + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceNativeTypeNameTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceNativeTypeNameTest.cs new file mode 100644 index 00000000..61ac6576 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceNativeTypeNameTest.cs @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information. + +using System.Threading.Tasks; +using NUnit.Framework; + +namespace ClangSharp.UnitTests.Baseline; + +[TestFixtureSource(nameof(Variants))] +public sealed class NamespaceNativeTypeNameTest : BaselineTest +{ + public NamespaceNativeTypeNameTest(BaselineVariant variant) : base(variant) + { + } + + protected override string Area => "NamespaceNativeTypeName"; + + // clang 22's type printer omits the enclosing C++ namespace from a reference spelled from within that + // same namespace, where-as older releases always spelled it. The generator restores the dropped + // `Namespace::` prefix from the decl so the emitted `NativeTypeName` keeps the fully qualified source + // spelling for typedef, record (including by-pointer), and enum references and stays version-stable. + [Test] + public Task NamespaceQualifiedNativeTypeNameIsPreservedTest() + { + var inputContents = @"namespace Ns +{ + typedef float Real; + + struct Point + { + Real X; + Real Y; + }; + + enum Status + { + Err = -1, + Ok = 0, + }; + + struct Holder + { + Point point; + Point* pointPtr; + Real r; + Status status; + }; +} +"; + + return ValidateAsync(nameof(NamespaceQualifiedNativeTypeNameIsPreservedTest), inputContents); + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceRemapTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceRemapTest.cs new file mode 100644 index 00000000..e238f120 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/NamespaceRemapTest.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information. + +using System.Collections.Generic; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace ClangSharp.UnitTests.Baseline; + +[TestFixtureSource(nameof(Variants))] +public sealed class NamespaceRemapTest : BaselineTest +{ + public NamespaceRemapTest(BaselineVariant variant) : base(variant) + { + } + + protected override string Area => "NamespaceRemap"; + + // A `--remap` keyed by the fully qualified name of a namespaced C++ type (e.g. `Ns.Point`) must apply to + // both the type declaration and every reference to it. The reference resolves through the same decl as the + // declaration, so a use-site field must emit the remapped name rather than the unqualified leaf, keeping the + // declaration and its references in agreement. + [Test] + public Task NamespaceQualifiedRemapAppliesAtUseSiteTest() + { + var inputContents = @"namespace Ns +{ + struct Point + { + int X; + int Y; + }; + + enum Status + { + Err = -1, + Ok = 0, + }; +} + +struct Holder +{ + Ns::Point point; + Ns::Point* pointPtr; + Ns::Status status; +}; +"; + + var remappedNames = new Dictionary { + ["Ns.Point"] = "NsPoint", + ["Ns.Status"] = "NsStatus", + }; + + return ValidateAsync(nameof(NamespaceQualifiedRemapAppliesAtUseSiteTest), inputContents, remappedNames: remappedNames); + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/SizeTMacroBindingTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/SizeTMacroBindingTest.cs new file mode 100644 index 00000000..1604f203 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/SizeTMacroBindingTest.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information. + +using System.Threading.Tasks; +using ClangSharp.UnitTests.Baseline; +using NUnit.Framework; + +namespace ClangSharp.UnitTests; + +/// Provides validation that a macro whose value is a size_t expression (clang 22 models this as a +/// PredefinedSugarType spelled __size_t) resolves to its underlying primitive rather than leaking the +/// internal type name or emitting a malformed ref readonly getter under unmanaged-constants. +[Platform("win")] +public sealed class SizeTMacroBindingTest : StandaloneBaselineTest +{ + protected override string Area => "SizeTMacroBinding"; + + [Test] + public Task SizeTMacroTest() + { + var inputContents = @"#define MY_INT_SIZE sizeof(int)"; + + return ValidateGeneratedCSharpLatestWindowsBaselineAsync(inputContents); + } + + [Test] + public Task SizeTGenerateUnmanagedConstantsMacroTest() + { + var inputContents = @"#define MY_INT_SIZE sizeof(int)"; + + return ValidateGeneratedCSharpLatestWindowsBaselineAsync(inputContents, PInvokeGeneratorConfigurationOptions.GenerateUnmanagedConstants); + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/UnalignedTypeBindingTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/UnalignedTypeBindingTest.cs new file mode 100644 index 00000000..e742de81 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/UnalignedTypeBindingTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information. + +using System.Threading.Tasks; +using ClangSharp.UnitTests.Baseline; +using NUnit.Framework; + +namespace ClangSharp.UnitTests; + +/// Provides validation that a pointer to an __unaligned-qualified record resolves to the record type +/// rather than leaking the qualifier and elaborated struct keyword (e.g. __unaligned struct tagFoo*), +/// which is not valid C#. +[Platform("win")] +public sealed class UnalignedTypeBindingTest : StandaloneBaselineTest +{ + protected override string Area => "UnalignedTypeBinding"; + + [Test] + public Task UnalignedRecordPointerParameterTest() + { + var inputContents = @"typedef struct tagMETARECORD { int rdSize; } METARECORD; +typedef struct tagMETARECORD __unaligned *LPMETARECORD; + +void PlayMetaFileRecord(LPMETARECORD lpMR);"; + + return ValidateGeneratedCSharpLatestWindowsBaselineAsync(inputContents); + } +}