Skip to content

Add MSTest reflection source generator (issue #1837)#8586

Open
Evangelink wants to merge 14 commits into
mainfrom
dev/amauryleve/sourcegen-reflection-issue-1837
Open

Add MSTest reflection source generator (issue #1837)#8586
Evangelink wants to merge 14 commits into
mainfrom
dev/amauryleve/sourcegen-reflection-issue-1837

Conversation

@Evangelink
Copy link
Copy Markdown
Member

@Evangelink Evangelink commented May 25, 2026

Fixes part of #1837.

What

Introduces a new Roslyn IIncrementalGenerator (project MSTest.SourceGeneration, under src/Analyzers/) that discovers [TestClass] types at compile time and emits a [ModuleInitializer] registering a SourceGeneratedReflectionDataProvider for the user's assembly.

It also adds the runtime infrastructure in MSTestAdapter.PlatformServices (folder SourceGeneration/) to swap the IReflectionOperations and IFileOperations services for source-gen-backed implementations once metadata is registered.

This is the first step toward Native AOT support: when the generator runs, MSTest reads test metadata from compile-time-known data instead of doing reflection at runtime. The feature is opt-in — when no metadata is registered, the platform keeps using the existing reflection-based implementations.

Why

Issue #1837 tracks Native AOT support for MSTest. PR #8263 introduced the IReflectionOperations service abstraction as a prerequisite. This PR delivers the next building block by adding the source generator that will populate that abstraction without reflection.

Layout

  • Generator (new project): src/Analyzers/MSTest.SourceGeneration/ReflectionMetadataGenerator and its emitters/models/helpers, packaged as analyzers/dotnet/cs.
  • Runtime hook & shims: src/Adapter/MSTestAdapter.PlatformServices/SourceGeneration/ReflectionMetadataHook, SourceGeneratedReflectionDataProvider, CompositeSourceGeneratedReflectionDataProvider, SourceGeneratedReflectionOperations, SourceGeneratedFileOperations, SourceGeneratorToggle.
  • Hook surface on PlatformServiceProvider: a single internal SetSourceGeneratedOperations method used by ReflectionMetadataHook to swap providers.
  • Removed: the legacy src/Adapter/MSTest.Engine/ project and its unit tests, which the new design replaces. (This is what shows up in the solution-file diff.)
  • Unit tests: test/UnitTests/MSTest.SourceGeneration.UnitTests/.
  • PublicAPI.Unshipped.txt updated.

Scope (MVP)

  • Generator emits a per-assembly [ModuleInitializer] that calls ReflectionMetadataHook.SetMetadata.
  • Composite provider supports multiple test assemblies registering concurrently.
  • Unit tests cover the happy path ([TestClass] + [TestMethod]), static/abstract classes being skipped, empty-assembly emission, and a Roslyn-compiles-cleanly smoke test.

Known follow-ups (out of scope for this PR)

These are intentionally deferred to keep this PR reviewable:

  • Populate the rest of the data bag (AssemblyAttributes, TypeAttributes, TypeProperties, TypeMethodAttributes, TypeConstructorsInvoker, TypeMethodLocations). The MVP only emits the assembly name, type list, and per-type method list.
  • [ModuleInitializer] polyfill for netstandard2.0 / older TFM consumers.
  • MSBuild gating (opt-in property like EnableMSTestSourceGeneration).
  • NuGet packaging of the generator into the MSTest analyzer set.
  • Wire up coverage of DataRow / DynamicData / inherited attributes / base-class roll-up.
  • AssemblyInitialize / ClassInitialize / TestInitialize lifecycle.

Validation

Built locally with build.cmd -c Debug (0 warnings, 0 errors).

Notes for reviewers

  • Public API is intentionally minimal: only ReflectionMetadataHook.SetMetadata and the SourceGeneratedReflectionDataProvider shape are public, because the generated module initializer needs to call them.
  • SourceGeneratorToggle uses Interlocked.Exchange to ensure exactly-once swap and to make the toggle race-free if multiple assemblies' module initializers run.
  • ReflectionMetadataGenerator skips static and abstract types — they cannot be discovered as test classes.
  • IReflectionOperations.GetType(string) delegates straight to the fallback so callers cannot bind to a same-named type from the wrong assembly via the composite TypesByName lookup; the assembly-qualified GetType(Assembly, string) overload is the one that uses the source-generated data.
  • Happy to split this into smaller PRs (e.g., runtime infra first, then generator) if that's easier to review.

Copilot AI review requested due to automatic review settings May 25, 2026 22:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an opt-in MSTest reflection metadata source generator (MSTestAdapter.PlatformServices.SourceGeneration) plus new runtime infrastructure in MSTestAdapter.PlatformServices to swap IReflectionOperations/IFileOperations to source-gen-backed implementations via a generated [ModuleInitializer]. This is intended as an early building block toward NativeAOT support (#1837).

Changes:

  • Add a new incremental generator project that discovers [TestClass]/[TestMethod] and emits a module initializer registering metadata.
  • Add runtime “source-generated” reflection/file operations and a public hook (ReflectionMetadataHook) for generated code to register metadata.
  • Add unit tests for the generator and wire new projects into TestFx.slnx and MSTest.slnf, plus update PublicAPI.Unshipped.txt.
Show a summary per file
File Description
TestFx.slnx Adds the new generator project + its unit test project to the full solution.
MSTest.slnf Adds the new generator project + its unit test project to the MSTest solution filter.
src/Adapter/MSTestAdapter.PlatformServices/PlatformServiceProvider.cs Adds internal hook to swap IReflectionOperations/IFileOperations.
src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Unshipped.txt Declares new public API surface (ReflectionMetadataHook, SourceGeneratedReflectionDataProvider).
src/Adapter/MSTestAdapter.PlatformServices/SourceGeneration/*.cs Adds runtime hook + source-gen-backed reflection/file operations + toggle.
src/Adapter/MSTestAdapter.PlatformServices.SourceGeneration/* Adds generator models/helpers + incremental generator + emitter + banned symbols.
test/UnitTests/MSTestAdapter.PlatformServices.SourceGeneration.UnitTests/* Adds generator unit tests + test runner program + test project.

Copilot's findings

  • Files reviewed: 19/19 changed files
  • Comments generated: 14

@Evangelink
Copy link
Copy Markdown
Member Author

🔍 Build Failure Analysis

Summary — The build failed due to a missing interface method and enforced code style violations across multiple target frameworks.

Root cause 1: Missing interface method SetSourceGeneratedOperations

The code in ReflectionMetadataHook.cs calls SetSourceGeneratedOperations on an IPlatformServiceProvider interface reference, but this method is not declared in the interface — it only exists as an internal method on the concrete PlatformServiceProvider class (line 181).

Affected files / errors

Proposed fix

Cast PlatformServiceProvider.Instance to the concrete type before calling the internal method:

-        PlatformServiceProvider.Instance.SetSourceGeneratedOperations(reflectionOperations, fileOperations);
+        ((PlatformServiceProvider)PlatformServiceProvider.Instance).SetSourceGeneratedOperations(reflectionOperations, fileOperations);

Alternative fix (if you want to expose this as part of the interface contract):

Add the method to IPlatformServiceProvider.cs:

     ITestContext GetTestContext(ITestMethod? testMethod, string? testClassFullName, IDictionary<string, object?> properties, IMessageLogger messageLogger, UTF.UnitTestOutcome outcome);
+
+    /// <summary>
+    /// Swaps the cached reflection and file operations with source-generated implementations.
+    /// </summary>
+    void SetSourceGeneratedOperations(IReflectionOperations reflectionOperations, IFileOperations fileOperations);
 }

And change the PlatformServiceProvider implementation from internal to public.


Root cause 2: Code style violations (IDE0032 — Use auto property)

Four fields are declared with explicit backing fields but could be converted to auto-properties. The analyzer rule IDE0032 is being enforced as an error.

Affected files / errors

Proposed fix

Convert explicit backing fields to auto-properties. These are readonly fields that are only assigned once in the constructor, so they can use the newer auto-property syntax with initializers.


Root cause 3: Code style violations (IDE0046 — Simplify conditional expression)

Three locations have if statements that can be simplified to conditional expressions. The analyzer rule IDE0046 is being enforced as an error.

Affected files / errors

Proposed fix

Simplify if/return patterns to ternary expressions or null-coalescing operators where appropriate.


Build overview
  • Project: MSTestAdapter.PlatformServices.csproj
  • Target frameworks: net8.0, net9.0 (errors occur in both)
  • Configuration: Debug
  • Exit code: 1 (failure)
  • Total unique errors: 7 distinct issues (multiplied across TFMs = 14 total occurrences)

The build failed during the CoreCompile target when compiling the newly added SourceGeneration code.

All MSBuild errors (14 occurrences)
Code Project File:Line Message
CS1061 MSTestAdapter.PlatformServices ReflectionMetadataHook.cs:37 'IPlatformServiceProvider' does not contain a definition for 'SetSourceGeneratedOperations'... (net8.0)
CS1061 MSTestAdapter.PlatformServices ReflectionMetadataHook.cs:37 'IPlatformServiceProvider' does not contain a definition for 'SetSourceGeneratedOperations'... (net9.0)
IDE0032 MSTestAdapter.PlatformServices SourceGeneratorToggle.cs:13 Use auto property (net8.0)
IDE0032 MSTestAdapter.PlatformServices SourceGeneratorToggle.cs:13 Use auto property (net9.0)
IDE0032 MSTestAdapter.PlatformServices SourceGeneratedReflectionOperations.cs:15 Use auto property (net8.0)
IDE0032 MSTestAdapter.PlatformServices SourceGeneratedReflectionOperations.cs:15 Use auto property (net9.0)
IDE0032 MSTestAdapter.PlatformServices SourceGeneratedFileOperations.cs:16 Use auto property (net8.0)
IDE0032 MSTestAdapter.PlatformServices SourceGeneratedFileOperations.cs:16 Use auto property (net9.0)
IDE0046 MSTestAdapter.PlatformServices SourceGeneratedReflectionOperations.cs:26 'if' statement can be simplified (net8.0)
IDE0046 MSTestAdapter.PlatformServices SourceGeneratedReflectionOperations.cs:26 'if' statement can be simplified (net9.0)
IDE0046 MSTestAdapter.PlatformServices SourceGeneratedReflectionOperations.cs:257 'if' statement can be simplified (net8.0)
IDE0046 MSTestAdapter.PlatformServices SourceGeneratedReflectionOperations.cs:257 'if' statement can be simplified (net9.0)
IDE0046 MSTestAdapter.PlatformServices SourceGeneratedReflectionOperations.cs:291 'if' statement can be simplified (net8.0)
IDE0046 MSTestAdapter.PlatformServices SourceGeneratedReflectionOperations.cs:291 'if' statement can be simplified (net9.0)

🤖 Generated by the Build Failure Analysis workflow · commit 6887de2

Generated by Build Failure Analysis for issue #8586 · ● 1.3M ·

Copy link
Copy Markdown
Member Author

@Evangelink Evangelink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by Build Failure Analysis for issue #8586 · ● 1.3M

@Evangelink Evangelink force-pushed the dev/amauryleve/sourcegen-reflection-issue-1837 branch from 6887de2 to 73050ee Compare May 28, 2026 16:46
Consolidates the experimental MSTest.Engine package into the existing MSTest.SourceGeneration package, addresses PR review comments, and adds correctness fixes for ref/out/in parameter signatures, inaccessible nested test classes, file-local test classes, open generic test classes, overridden method deduplication, and composite-provider null handling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 28, 2026 17:03
@Evangelink Evangelink force-pushed the dev/amauryleve/sourcegen-reflection-issue-1837 branch from 73050ee to a670c54 Compare May 28, 2026 17:03
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 171/172 changed files
  • Comments generated: 2

Comment thread TestFx.slnx
- Delegate IReflectionOperations.GetType(string) to fallback so simple
  type names cannot bind to a same-named type from the wrong assembly
  via the composite TypesByName lookup (matches Type.GetType semantics).
- Simplify HasByRefParameter to a single LINQ Any() expression.
- Drop unused reassignment of GeneratorDriver in the generator test
  harness's RunGeneratorAndGetCompilation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink
Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

…gen-reflection-issue-1837

# Conflicts:
#	src/Adapter/MSTest.Engine/Engine/TestFrameworkEngine.cs
#	src/Adapter/MSTest.Engine/PublicAPI/PublicAPI.Unshipped.txt
#	src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionParameterizedTestNode.cs
#	src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeActionTaskParameterizedTestNode.cs
#	src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionParameterizedTestNode.cs
#	src/Adapter/MSTest.Engine/TestNodes/InternalUnsafeAsyncActionTaskParameterizedTestNode.cs
#	test/UnitTests/MSTest.Engine.UnitTests/BFSTestNodeVisitorTests.cs

Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 1, 2026 08:04
@Evangelink Evangelink review requested due to automatic review settings June 1, 2026 08:04
Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 1, 2026

@copilot resolve the merge conflicts in this pull request

Merge conflicts resolved in 8578ce3. All conflicts were "deleted by us" — files in MSTest.Engine that our PR intentionally removed but main had modified. I kept our deletions and also removed the new InternalUnsafeParameterizedTestNodeBase.cs file that main added to the deleted directory. All other changes from main were auto-merged cleanly.

…flection

- ReflectionMetadataEmitter: emit [DynamicDependency(All, typeof(T))] per test
  class on the [ModuleInitializer], so the trimmer keeps constructors and other
  reflected members alive (otherwise discovery fails with 'Cannot find a valid
  constructor for test class').
- ReflectionMetadataEmitter: annotate ResolveMethod's Type parameter with
  [DynamicallyAccessedMembers(PublicMethods | NonPublicMethods)] to satisfy
  IL2070 in the generated module initializer.
- SourceGeneratedReflectionOperations: stop routing fallback through
  _fallback.GetCustomAttributesCached. ReflectionOperations.NotCachedReflectionAccessor
  reads PlatformServiceProvider.Instance.ReflectionOperations, which after
  SetMetadata is the source-gen wrapper itself -- causing infinite mutual recursion
  and a StackOverflowException at runtime. Use _fallback.GetCustomAttributes
  (direct reflection) instead.
- MSTest.Sdk NativeAOT.targets: add MSTest.TestAdapter package reference and set
  EnableMSTestRunner/IsTestingPlatformApplication = true (mirroring ClassicEngine.targets)
  so MSTestAdapter.PlatformServices.dll (the source-generator runtime hook host) is
  available to NAOT-published apps.
- NativeAotTests / SdkTests / TrimTests: tolerate upstream IL warnings from
  Microsoft.TestPlatform.ObjectModel and System.Private.DataContractSerialization
  (warnAsError: false) and assert via shared TrimAndAotAssertions.MSTestOwnedSourceFiles
  that MSTest-owned source files do not appear in publish output, mirroring the
  pattern established in PR #8686. Rename Publish_ShouldNotProduceTrimWarnings to
  Publish_WithSourceGeneration_DoesNotSurfaceMSTestOwnedTrimWarnings.
- NativeAotTests: use AssertOutputContainsSummary helper (current MTP output format).
- samples/NativeAotRunner/TestProject1: convert to MSTest.Sdk shape and drop the
  pinned MSTest.SourceGeneration 2.0.0-alpha.26228.3 reference (which emitted now-
  removed Microsoft.Testing.Framework.TestNode types and broke the WindowsSamples
  CI legs).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 1, 2026 13:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 174/175 changed files
  • Comments generated: 1

Evangelink and others added 2 commits June 1, 2026 16:56
A [TestMethod] declared as Test<T>(T value) would have its parameter
types collected as 	ypeof(T), but the generated module initializer is
non-generic, so T does not bind there and the compilation fails.
Reflection mode handles generic test methods at runtime, so opting into
the generator must not turn a valid program into a build error.

Add an IsGenericMethod guard alongside the existing open-generic-class
and by-ref-parameter guards, and add a unit test covering both the
generic-with-params and generic-without-params shapes.

Addresses PR review feedback:
#8586 (comment)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 3, 2026 12:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 175/176 changed files
  • Comments generated: 5

…er non-AOT scenario

Unit tests:
- Replace the canonical generator test with a full `.Should().Be(expected)` snapshot
  match so the entire emitted file (header, hint name, namespaces, attributes, module
  initializer, partial class layout, `TypeMethods`/`Types`/`TypesByName` blocks,
  `DynamicDependency` attributes) is now verified verbatim instead of relying on a
  handful of `Contain` checks.
- Refactor the overload test to assert against a contiguous block of the generated
  `TypeMethods` payload rather than separate `Contain` fragments, so reordering or
  whitespace regressions in the generator are surfaced immediately.
- Add tests for: stable hint-name, multiple test classes emitted in source order, test
  classes declared in the global namespace, methods discovered via a user-defined
  `TestMethodAttribute` subclass, and `internal` test classes. Extend the by-ref
  parameter skip test to also cover `ref readonly`.
- Add a `NormalizeNewlines` helper so snapshots compare apples-to-apples on Windows
  (generator emits `\n`; raw string literals in test sources contain `\r\n`).

Non-AOT acceptance coverage:
- New `SourceGenerationNonAotTests` pins that `MSTest.SourceGeneration` works in a
  plain SDK project with no `PublishAot`/`PublishTrimmed`. It builds the asset, asserts
  the generator emitted `<assembly>.MSTestReflectionMetadata.g.cs` via
  `EmitCompilerGeneratedFiles`, and runs the test host to confirm tests are discovered
  and executed through the source-generated `ReflectionMetadataHook`.

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

- Revert sample TestProject1.csproj change

- SourceGeneratedFileOperations.LoadAssembly: fall back to inner provider for unregistered assemblies

- GetDeclaredMethods/GetRuntimeMethods: always delegate to fallback since TypeMethods is partial

- CompositeSourceGeneratedReflectionDataProvider: snapshot pattern with Volatile.Read/Write for thread-safe publishing

- Reduce public API to fluent builder: ReflectionMetadataHook.ForAssembly(...).WithTypes(...).WithTestMethods(...).Register() (43 -> 7 entries)

- TypeMethodAttributes: key by MethodInfo (was string) now that the type is internal

- Update generator to emit fluent-builder calls

- Document inherited-[TestClass] limitation in XML docs on ReflectionMetadataHook

- Add MSTEST0069 analyzer (InheritedTestClassAttributeWithSourceGeneratorAnalyzer) shipped in MSTest.SourceGeneration package to warn when [TestClass] is only inherited from a base class

- Add release tracking files for the new analyzer

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 3, 2026 16:31
@Evangelink
Copy link
Copy Markdown
Member Author

Pushed e8a3e60 addressing the latest round of review feedback plus the asks about reducing the public-API surface and documenting the inherited-[TestClass] limitation:

Reduced public API (43 -> 7 entries in PublicAPI.Unshipped.txt)

  • SourceGeneratedReflectionDataProvider (and its nested types) are now internal.
  • New fluent builder: ReflectionMetadataHook.ForAssembly(assembly).WithTypes(...).WithTestMethods(...).Register().
  • Source generator updated to emit the new shape.
  • TypeMethodAttributes re-keyed from string -> MethodInfo (safe now that the type is internal; fixes the overload-ambiguity concern).

Fixes for the runtime contract issues

  • SourceGeneratedFileOperations.LoadAssembly: falls back to the inner provider for unregistered assemblies (mixed-mode no longer throws).
  • GetDeclaredMethods / GetRuntimeMethods: always delegate to the fallback provider since the generator only emits [TestMethod]-annotated methods.
  • CompositeSourceGeneratedReflectionDataProvider: rewritten with an immutable snapshot pattern (Volatile.Read/Volatile.Write publishing of a CompositeState record). Readers can no longer observe a torn intermediate state during Add.
  • Reverted unrelated change to samples/.../TestProject1.csproj.

Documented + analyzed the inherited-attribute limitation

  • Added a Discovery limitation section to the XML docs on ReflectionMetadataHook calling out that [TestClass] is only honored when declared directly on the type (inherited [TestClass] is silently skipped by the generator).
  • New analyzer MSTEST0069 in the MSTest.SourceGeneration package: InheritedTestClassAttributeWithSourceGeneratorAnalyzer. Warns on classes that would only be picked up via attribute inheritance. Shipped only with the source-generation package, so the warning fires only for users who have opted in to source-gen discovery.
  • Diagnostic ID reserved in DiagnosticIds.cs to avoid future collisions.
  • Unit tests cover direct attribute, inherited from direct base, inherited from grandparent, abstract base, and subclass-of-TestClassAttribute.

Build + MSTest.SourceGeneration.UnitTests are green locally. Will address the remaining individual conversation threads inline once we agree on the new shape.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 181/182 changed files
  • Comments generated: 1

Evangelink and others added 3 commits June 3, 2026 22:42
…gen-reflection-issue-1837

# Conflicts:
#	src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs
…ter method

Replaces the fluent MetadataBuilder API (4 public entries) with a single
static Register(Assembly, Type[], IReadOnlyDictionary<Type, MethodInfo[]>)
method. Defensive copies, TypesByName computation, snapshot creation, and
lock-protected publish all happen inline. PublicAPI.Unshipped.txt drops from
7 to 3 lines.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The MSTest source generator skips static, abstract, open-generic, and
file-local classes. Mirror the same filtering in the analyzer to avoid
misleading warnings on types that wouldn't become discoverable even after
applying [TestClass] directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 3, 2026 21:00
@Evangelink
Copy link
Copy Markdown
Member Author

Update: addressed the open review threads and pushed three commits on top of the rebase to main.

Summary of changes since the original push

  • e8a3e605a — Initial review-feedback batch: LoadAssembly fallback, GetRuntimeMethods -> _fallback, GetDeclaredMethods always delegates to fallback, CompositeSourceGeneratedReflectionDataProvider rebuilt around an immutable CompositeState snapshot, SourceGeneratedReflectionDataProvider (+ TypeMethodAttributes) made internal, public API reduced from 43 to a small fluent builder, MSTEST0069 analyzer added (InheritedTestClassAttributeWithSourceGenerator) plus tests, NativeAotRunner/TestProject1.csproj revert.
  • ed5b24a83 — Merge origin/main (one conflict in DiagnosticIds.cs between MSTEST0068 and the MSTEST0069 reservation; both kept).
  • b16f25f6b — Per the "avoid adding new public APIs on platform services" feedback, collapsed the fluent MetadataBuilder down to a single static ReflectionMetadataHook.Register(Assembly, Type[], IReadOnlyDictionary<Type, MethodInfo[]>). The public surface for this PR is now just 3 lines of PublicAPI.Unshipped.txt (one type, one method, plus #nullable enable).
  • 2959f75ac — Filter open-generic and file-local types from MSTEST0069 so the analyzer mirrors the source generator''s skip rules; added tests for both.

Documented limitation. ReflectionMetadataHook XML docs explicitly call out that the source generator only enumerates types with [TestClass] declared directly on the type (inherited [TestClass] is not picked up). MSTEST0069 flags any class that hits this case.

Full repo build is green (Build succeeded. 0 Warning(s) 0 Error(s)); the MSTest.SourceGeneration.UnitTests suite passes.

…ver)]

The single public Register entry point exists only because the source
generator's [ModuleInitializer] needs to call across the assembly
boundary into MSTestAdapter.PlatformServices. Hand-written code should
never use it. Marking the type and method as EditorBrowsable.Never plus
strengthening the XML docs makes that intent obvious to anyone browsing
the API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 180/181 changed files
  • Comments generated: 1

Comment thread src/Analyzers/MSTest.SourceGeneration/Models/TestAssemblyMetadata.cs Outdated
Comment thread src/Adapter/MSTestAdapter.PlatformServices/SourceGeneration/ReflectionMetadataHook.cs Dismissed
Evangelink and others added 2 commits June 4, 2026 00:19
Returning IEnumerator<T> from a struct enumerator goes through an
IEnumerable<T> cast and allocates on every foreach. The source-generator
pipeline iterates metadata.Classes, cls.Methods, and method.ParameterTypes
on every incremental tick, so this was a real allocation hot spot.

Return ImmutableArray<T>.Enumerator (a struct) by value so the foreach
binds to the duck-typed pattern with no boxing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The toggle's UseSourceGenerator getter was never read; Enable() was
called once from ReflectionMetadataHook.Register but had no observable
effect. The actual provider swap is done in the same code path via
PlatformServiceProvider.SetSourceGeneratedOperations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 4, 2026 12:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 179/180 changed files
  • Comments generated: 2

Comment on lines +81 to +85
public MethodInfo? GetRuntimeMethod(Type declaringType, string methodName, Type[] parameters, bool includeNonPublic)
{
foreach (MethodInfo method in GetRuntimeMethods(declaringType))
{
if (method.Name != methodName)
Comment on lines +135 to +139
// AssemblyName / Assembly do not make sense for a composite; leave at defaults.
var types = new List<Type>();
var typesByName = new Dictionary<string, Type>(StringComparer.Ordinal);
var typeAttributes = new Dictionary<Type, Attribute[]>();
var assemblyAttributes = new List<object>();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants