### Description The `[OptionsValidator]` source generator emits property and type names verbatim into **identifier position** without escaping C# keywords. If a validated options model has a property whose name is a keyword (declared with `@`, e.g. `@class`, `@event`, `@namespace`), the generated validator emits e.g. `options.class` instead of `options.@class`, which does not compile. ### Repro (self-contained library) `MyOptions.cs`: ```csharp using System.ComponentModel.DataAnnotations; using Microsoft.Extensions.Options; public class MyOptions { // Property whose name is a C# keyword; in source it must be written as @class. [Required] public string? @class { get; set; } } [OptionsValidator] public partial class MyOptionsValidator : IValidateOptions<MyOptions>; ``` `KeywordRepro.csproj`: ```csproj <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Extensions.Options" Version="10.*" /> </ItemGroup> </Project> ``` ### Actual behavior Build fails. The generated `Validators.g.cs` contains (note the unescaped `options.class`): ```csharp context.MemberName = "class"; // string literal — OK context.DisplayName = string.IsNullOrEmpty(name) ? "MyOptions.class" : $"{name}.class"; validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A1); if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.class, context, validationResults, validationAttributes)) // ^^^^^ should be options.@class ``` This causes confusing compiler errors (cascading from the unescaped keyword): ``` error CS1520: Method must have a return type error CS0501: 'MyOptionsValidator.MyOptionsValidator(validationResults)' must declare a body because it is not marked abstract, extern, or partial error CS0246: The type or namespace name 'validationResults' could not be found ``` Reproduced with `Microsoft.Extensions.Options` `10.0.9`. ### Expected behavior The generated code compiles, emitting `options.@class` (or otherwise escaping keyword identifiers). ### Root cause Two independent spots in `src/libraries/Microsoft.Extensions.Options/gen`: 1. **Member names (the one this repro hits).** `ValidatedMember.Name` is set to the raw `member.Name` in `Parser.cs` (`GetMemberInfo`), and `Emitter.cs` interpolates it directly into identifier position in ~15 places (e.g. `options.{vm.Name}` at the sync `TryValidateValue` call and the async `TryValidateValueAsync` call). No `@`-escaping is applied. 2. **Type/namespace FQNs.** `GetFQN` (`Parser.cs`) uses `SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier)`. `.WithMiscellaneousOptions(...)` **replaces** the option set, dropping the default `EscapeKeywordIdentifiers`. So a model type or namespace segment that is a keyword would also emit unescaped. (By contrast, `GetMinimalFQN`/`GetMinimalFQNWithoutGenerics` use `.AddGenericsOptions`/`.WithGenericsOptions` and therefore keep `EscapeKeywordIdentifiers` — an inconsistency.) ### Suggested fix - For member access, escape the leaf identifier before emission (e.g. prefix `@` when `SyntaxFacts.GetKeywordKind`/`GetContextualKeywordKind` indicates a keyword, or render via a `SymbolDisplayFormat` that has `EscapeKeywordIdentifiers`). - For `GetFQN`, use `.AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier)` instead of `.WithMiscellaneousOptions(...)` so `EscapeKeywordIdentifiers` is retained. ### Notes The in-flight async validation work (`ValidateAsync` emission) in #130263 uses the same `options.{vm.Name}` pattern, so it is affected identically and should be fixed together. > [!NOTE] > This issue was generated with the assistance of AI (GitHub Copilot).