Skip to content

Validate array/span parameters in the WinMD generator and add failure tests#2443

Open
Sergio0694 wants to merge 11 commits into
staging/3.0from
user/sergiopedri/winmd-array-param-validation
Open

Validate array/span parameters in the WinMD generator and add failure tests#2443
Sergio0694 wants to merge 11 commits into
staging/3.0from
user/sergiopedri/winmd-array-param-validation

Conversation

@Sergio0694

Copy link
Copy Markdown
Member

Summary

Fix several ways the WinMD generator (cswinrtwinmdgen) mishandled array/span method parameters when authoring a Windows Runtime component, add validation that rejects the shapes with no Windows Runtime representation, and stand up a new WinMDGeneratorTest project that exercises the generator's failure modes end-to-end.

Motivation

When authoring a component, the WinMD generator maps each method parameter to a Windows Runtime array convention (ReadOnlySpan<T> → PassArray, Span<T> → FillArray, out T[] → ReceiveArray). A few parameter shapes were handled incorrectly:

  • Custom modifiers were never stripped before dispatching on the parameter type, so any modreq-wrapped signature fell through to the System.Object fallback and was silently erased. This affected in parameters on abstract/virtual/interface/delegate members — most notably a COM interop in Guid riid was emitted as [in] object instead of [in] Guid&.
  • ref/in arrays and out spans have no Windows Runtime representation, but were silently emitted as invalid metadata rather than reported as errors.

There was also no test coverage for the generator's failure behavior. Because these are build-time failures, they could not be asserted from the existing build-integrated test projects without breaking the build — so a dedicated end-to-end test project was needed.

Changes

  • src/WinRT.WinMD.Generator/Writers/WinMDWriter.TypeMapping.cs: strip custom modifiers (modreq/modopt) before mapping a type signature, so modifier-wrapped types are no longer erased to object.
  • src/WinRT.WinMD.Generator/Writers/WinMDWriter.Members.cs, WinMDWriter.Types.cs, Errors/WellKnownWinMDExceptions.cs: validate parameter conventions and reject the unsupported shapes with clear errors — ref/in arrays (CSWINRTWINMDGEN0011) and by-reference spans such as out Span<T> (CSWINRTWINMDGEN0012) — while preserving the deliberate COM interop ref/in scalar behavior (e.g. ref Guid riid[in]).
  • src/WinRT.WinMD.Generator/Extensions/TypeSignatureExtensions.cs: new extension file housing StripCustomModifiers, IsTypeOfSpan, and IsTypeOfReadOnlySpan (consistent with the interop generator's IsTypeOf* extensions), moved out of the writer partials.
  • src/Tests/WinMDGeneratorTest/: new MSTest project that runs the actual cswinrtwinmdgen tool as a subprocess. Test_ParameterConventions covers the unsupported array/span shapes; Test_InvalidInputs covers invalid invocations (malformed/missing response files, bad arguments, missing/corrupt input assemblies, an unwritable output path, a missing debug-repro directory). The WinMDGeneratorRunner helper exposes single-call AssertSuccess/AssertFailure entry points; valid .winmd shapes remain covered by the authoring tests.
  • src/cswinrt.slnx: register the new test project.
  • .github/skills/testing/SKILL.md, .github/skills/update-testing-instructions/SKILL.md: document the new test project (new section, routing-table row, count bump) and add a matching per-project verification step to the update skill.

Sergio0694 and others added 9 commits June 16, 2026 11:42
MapTypeSignatureToOutput had no handling for CustomModifierTypeSignature, so any modifier-wrapped type fell through to the 'System.Object' fallback and was silently erased. This affects 'in' parameters on abstract/virtual/interface/delegate members, where the C# compiler emits a 'modreq(InAttribute)' on the by-reference type. For example, a COM interop 'in Guid riid' was emitted as '[in] object' instead of '[in] Guid&'. Strip leading custom modifiers before dispatching on the underlying type.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nerator

Windows Runtime arrays use one of three conventions (ReadOnlySpan<T> for PassArray, Span<T> for FillArray, or 'out T[]' for ReceiveArray), and spans are always passed by value. The generator previously emitted invalid metadata for a few shapes: 'ref T[]'/'in T[]' became '[in] T[]&', and 'out Span<T>'/'out ReadOnlySpan<T>' became a ReceiveArray. Reject these with clear errors (CSWINRTWINMDGEN0011 for by-reference arrays, CSWINRTWINMDGEN0012 for by-reference spans), while preserving the deliberate COM interop 'ref'/'in' scalar behavior (e.g. 'ref Guid riid' -> '[in]') and the by-value array PassArray default.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a WinMDGeneratorTest project that runs the actual cswinrtwinmdgen tool end-to-end: it compiles small C# inputs with Roslyn, invokes the generator as a subprocess, and asserts a non-zero exit code plus the expected CSWINRTWINMDGEN error. The project deliberately focuses on failure cases (unsupported 'ref'/'in' arrays and 'out' spans), which can be exercised here without failing the build; the supported '.winmd' shapes (PassArray/FillArray/ReceiveArray) are covered by the authoring tests. The generator exposes no internals to the test project. Registers the project in the solution.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extend the WinMDGeneratorTest harness with a custom-response-file overload and a public CompileComponent helper, then add Test_InvalidInputs covering the generator's remaining end-to-end failure modes: a missing response file (CSWINRTWINMDGEN0001), a malformed or duplicate-argument response file (0002), a missing required or unparseable argument (0003), a missing or corrupt input assembly (0004), an unwritable output path (0006), and a missing debug-repro directory (0008).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move StripCustomModifiers (previously a private method in WinMDWriter.TypeMapping) and the span predicates into a new Extensions/TypeSignatureExtensions.cs, matching the existing TypeDefinitionExtensions pattern. Rename IsSpan/IsReadOnlySpan to IsTypeOfSpan/IsTypeOfReadOnlySpan as extension methods, consistent with the interop generator's IsTypeOf* extensions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ests

Rename the test helper to WinMDGeneratorRunner and give it AssertSuccess/AssertFailure entry points that run the generator and make the exit-code and error-output assertions, so each test is a single call. The run mechanics (Run, RunTool, GetGeneratorPath, the failure-result assertion, cleanup) are private; only the assertion entry points and CompileComponent (used by the response-file factory scenarios) are public.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… tests

Change the AssertFailure response-file factory from Func<string, IReadOnlyList<string>> to Func<string, string> so callers provide the entire '.rsp' content as one multiline raw interpolated string instead of a list of per-line expressions. The runner now writes the content verbatim with File.WriteAllText.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a 'WinMD generator tests' section describing the new src/Tests/WinMDGeneratorTest project (end-to-end failure-case tests for cswinrtwinmdgen), add a routing-table row, and bump the primary test area count to 6. Also add a matching per-project verification step to the update-testing-instructions skill and renumber the subsequent steps.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The tests run the generator tool end-to-end as a subprocess rather than exercising its managed logic in-process, so update the comment that justifies 'CsWinRTEnabled=false' to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Sergio0694 Sergio0694 added authoring Related to authoring feature work testing Related to testing infrastructure cswinmd tooling CsWinRT 3.0 labels Jun 16, 2026
@Sergio0694 Sergio0694 requested a review from manodasanW June 16, 2026 20:50
Define the by-reference array/span parameter errors (0011/0012) after the debug-repro errors (0008-0010) instead of in the middle, so the methods appear in ascending error-id order.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Sergio0694 Sergio0694 marked this pull request as ready for review June 16, 2026 20:57
…TSDK1151)

The generator's PublishAot build is self-contained, which a non self-contained
executable cannot reference (NETSDK1151). Replace GlobalPropertiesToRemove for
RuntimeIdentifier with UndefineProperties for BuildToolArch, PublishBuildTool,
RuntimeIdentifier and SelfContained so the tool is built framework-dependent for
the build host, matching the proven WinRT.Internal reference pattern. The output
stays under net10.0 (no RID subfolder), so the tool path used by the tests is
unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

authoring Related to authoring feature work cswinmd CsWinRT 3.0 testing Related to testing infrastructure tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant