Skip to content

Fix ValidationsGenerator ITypeParameterSymbol handling#65419

Open
ANcpLua wants to merge 2 commits intodotnet:mainfrom
ANcpLua:fix/validations-generator-type-parameter-handling
Open

Fix ValidationsGenerator ITypeParameterSymbol handling#65419
ANcpLua wants to merge 2 commits intodotnet:mainfrom
ANcpLua:fix/validations-generator-type-parameter-handling

Conversation

@ANcpLua
Copy link

@ANcpLua ANcpLua commented Feb 13, 2026

Fixes #65418

Summary

The ValidationsGenerator silently skips generic type parameters (ITypeParameterSymbol) and can emit invalid typeof(T) expressions when type parameters leak into ValidatableProperty.Type.

Changes

Single file: src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs

  1. ITypeParameterSymbol branch in TryExtractValidatableType — recognizes type parameters and walks their ConstraintTypes to discover concrete validatable types. Adds to visitedTypes before iterating constraints to prevent infinite recursion through circular constraints (e.g., where T : class, IEnumerable<T>).

  2. ContainsTypeParameter guards — two guards (record property path + regular property path) that skip properties whose type contains an unresolved type parameter anywhere in the type tree. Without these, the emitter would generate typeof(TSelf) for CRTP patterns like RequestBase<TSelf>.

  3. ContainsTypeParameter helper — recursively checks ITypeParameterSymbol, IArrayTypeSymbol, and INamedTypeSymbol.TypeArguments to catch T, T[], List<T>, Dictionary<string, T>, Nullable<T>, etc.

Why all four blocks are one fix

  • The ITypeParameterSymbol branch alone (without guards) causes a regression: constraint walking discovers types whose properties contain the type parameter, leading to typeof(TSelf) in emitted code.
  • The guards alone (without the branch) would silently drop the properties but never discover the validatable types through constraints.
  • The helper is required by the guards.

Test plan

  • CRTP pattern CommandBase<TSelf> where TSelf : CommandBase<TSelf> with concrete derived types validates correctly (class + record paths)
  • Base type properties ([Required]) and derived type properties ([Range]) discovered through CRTP inheritance
  • Runtime endpoint verification confirms validation errors are produced correctly
  • Existing validation tests continue to pass (CI)

…eters

When endpoint handlers use generic extension methods (e.g.,
MapCommand<TRequest>()), the resolved method has ITypeParameterSymbol
parameters. These have DeclaredAccessibility == NotApplicable, which
fails the accessibility check in TryExtractValidatableType, causing the
type to be silently skipped without any diagnostic.

Handle ITypeParameterSymbol before the accessibility check by walking
constraint types to discover validatable concrete types. Add the type
parameter to visitedTypes before iterating constraints to prevent stack
overflow with circular constraints. Add ContainsTypeParameter guard in
ExtractValidatableMembers to prevent invalid typeof() expressions in
generated code for properties whose types contain unresolved type
parameters (e.g., CRTP patterns).
Copilot AI review requested due to automatic review settings February 13, 2026 09:51
@github-actions github-actions bot added the needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically label Feb 13, 2026
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Feb 13, 2026
@dotnet-policy-service
Copy link
Contributor

Thanks for your PR, @@ANcpLua. Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

Copy link
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 fixes a critical bug in the ValidationsGenerator source generator where generic type parameters (like TRequest from a generic endpoint extension method) were either silently skipped or caused compilation errors when they leaked into generated typeof() expressions. The fix adds type parameter handling to discover validatable types through constraint types, while preventing type parameters from appearing in emitted code.

Changes:

  • Added ITypeParameterSymbol branch in TryExtractValidatableType to walk constraint types and discover validatable types
  • Added ContainsTypeParameter guards in both record and regular property extraction paths to prevent emitting typeof(T) expressions
  • Added ContainsTypeParameter helper method to recursively detect type parameters in type trees

Comment on lines +77 to +94
// Type parameters (e.g., TRequest from a generic MapCommand<TRequest>() extension)
// have DeclaredAccessibility == NotApplicable. The concrete type is only known at
// call sites, not inside the generic method body where the endpoint delegate is
// defined. Walk constraint types to discover any validatable types reachable
// through type constraints.
if (typeSymbol is ITypeParameterSymbol typeParam)
{
// Add to visitedTypes BEFORE iterating constraints to prevent
// infinite recursion through circular constraints such as
// where T : class, IEnumerable<T> (SEC-001).
visitedTypes.Add(typeSymbol);
var foundValidatable = false;
foreach (var constraintType in typeParam.ConstraintTypes)
{
foundValidatable |= TryExtractValidatableType(constraintType, wellKnownTypes, ref validatableTypes, ref visitedTypes);
}
return foundValidatable;
}
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

This PR fixes a critical bug that would cause compilation errors when type parameters leak into generated code. However, there are no tests covering the scenarios described in the PR description and issue #65418:

  1. Generic endpoint extension MapCommand<TRequest>() with constrained type parameter
  2. CRTP pattern RequestBase<TSelf> where TSelf : RequestBase<TSelf>
  3. Circular constraints where T : class, IEnumerable<T>
  4. Edge cases like List<T>, T[], Dictionary<string, T>, Nullable<T>

The existing test files in src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ show comprehensive test coverage for other features (records, polymorphism, IValidatableObject, etc.), but there's no test file for generic type parameters.

Without tests, there's a risk that:

  • Future changes could break this fix
  • The fix may not actually work in all the scenarios it's intended to handle
  • Edge cases may not be covered

Please add tests that verify:

  • Type parameters in endpoint handler signatures are discovered through constraints
  • CRTP patterns don't emit typeof(TSelf)
  • Circular constraints don't cause stack overflow
  • Properties with type parameter types are correctly skipped in both record and class code paths

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

Added tests in e20d0e4 — covers CRTP class and record patterns with runtime endpoint verification for Required and Range attributes.

@martincostello martincostello added area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-validation Issues related to model validation in minimal and controller-based APIs and removed needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically labels Feb 13, 2026
Verifies that types using the Curiously Recurring Template Pattern
(CommandBase<TSelf> where TSelf : CommandBase<TSelf>) are correctly
discovered and validated through the inheritance hierarchy.

Covers both class and record CRTP patterns with runtime endpoint
verification for Required and Range validation attributes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ANcpLua
Copy link
Author

ANcpLua commented Feb 13, 2026

@ANcpLua please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@dotnet-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@dotnet-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@dotnet-policy-service agree company="Microsoft"

Contributor License Agreement

@dotnet-policy-service agree

@ANcpLua
Copy link
Author

ANcpLua commented Feb 13, 2026

CI failures are pre-existing infrastructure flakes, unrelated to this change:

Happy to re-run if needed.

@azure-pipelines
Copy link

Commenter does not have sufficient privileges for PR 65419 in repo dotnet/aspnetcore

1 similar comment
@azure-pipelines
Copy link

Commenter does not have sufficient privileges for PR 65419 in repo dotnet/aspnetcore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc community-contribution Indicates that the PR has been added by a community member feature-validation Issues related to model validation in minimal and controller-based APIs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ValidationsGenerator crashes on generic type parameters (ITypeParameterSymbol)

2 participants