diff --git a/docs/wiki/Type-Library.md b/docs/wiki/Type-Library.md index b8e1f73..d576dfd 100644 --- a/docs/wiki/Type-Library.md +++ b/docs/wiki/Type-Library.md @@ -170,6 +170,27 @@ type — `byte`, `sbyte`, `short`, `ushort`, `int` (default), `uint`, `long` or `EnumValueDefinition.UnderlyingType`, and `EnumValueDefinition.Value` is stored as a `decimal` so every underlying type (including `ulong.MaxValue`) is represented exactly. +#### Declaring values inline on the enum's `[TypeRef]` field + +When the enum type is declared by a `[TypeRef]` in the same spec, `[EnumValue]` can be applied directly on +that `[TypeRef]` field — the enum type is inferred from the sibling `[TypeRef]` and each attribute's first +argument is the enum member name. This works even when the enum type is not available as a compile-time +type, and multiple `[EnumValue]` attributes may be declared on the field (`AllowMultiple`): + +```csharp +[GenerateTypeLibrary(ClassName = "TypeLibrary", Namespace = "MyGenerator")] +static partial class TypeLibraryModel +{ + [TypeRef("Aspire.Hosting.AspireC4.LikeC4Severity", GenerateFullNameConst = true)] + [EnumValue("Inherit", 0)] + [EnumValue("Warning", 3)] + static readonly TypeIdentity LikeC4Severity = default; +} +``` + +This is equivalent to declaring the enum type with a standalone `[TypeRef]` marker and each value with a +separate `[EnumValue]` marker field; the two styles can be mixed for the same enum. + The generator emits a nested `public static partial class {EnumName}Values` alongside the enum's `TypeIdentity`: ```csharp @@ -218,6 +239,34 @@ Enum value marker fields must be declared `private static readonly` (`TLB0008`), `= default` (`TLB0010`), and use a `TypeIdentity` or `EnumValueDefinition` field type (`TLB0016`). Duplicate member names in a group are `TLB0018`; duplicate numeric values are flagged as `TLB0019`. +#### Declaring all values of an available enum type + +When the enum type is available at compile time, use `[EnumValues(typeof(...))]` to declare every member in +one marker instead of listing individual `[EnumValue]` markers. The enum type must still be declared by a +sibling `[TypeRef]` marker in the same namespace (`TLB0017`), and the referenced type must actually be an +enum (`TLB0020`): + +```csharp +[GenerateTypeLibrary(ClassName = "TypeLibrary", Namespace = "MyGenerator")] +static partial class TypeLibraryModel +{ + [TypeRef("ServiceLifetime", "MyGenerator", GenerateFullNameConst = true)] + static readonly TypeIdentity ServiceLifetime = default; + + // Enum type is available, so one marker declares Singleton, Scoped, Transient, ... + [EnumValues(typeof(ServiceLifetime))] + static readonly TypeIdentity ServiceLifetimeValues = default; +} +``` + +The generator emits the same `{EnumName}Values` nested class that the equivalent `[EnumValue]` markers +produce: a `EnumValueDefinition` field per enum member (with its numeric value and underlying type taken +from the enum declaration, and XML documentation copied from each member), the per-value `{Member}FullName` +constants when the enum's `[TypeRef]` sets `GenerateFullNameConst`, and the `Get(string)` matcher. The +marker field's name is arbitrary and ignored — the member names come from the enum itself. The two +declaration styles can be combined for the same enum; an `[EnumValues]` marker that collides with an +existing member or another bulk marker is flagged (`TLB0018`). + ### Using full-name constants as attribute-data model targets A `[TypeRef]` member declared with `GenerateFullNameConst` produces a `public const string {Member}FullName` @@ -284,7 +333,7 @@ Set the MSBuild property `DisablePurviewTypeLibraryGenerator` to `true` to disab ## Validation -`TypeLibraryValidationAnalyzer` reports `TLB0001`–`TLB0019` for invalid specs, enum value members, and +`TypeLibraryValidationAnalyzer` reports `TLB0001`–`TLB0020` for invalid specs, enum value members, and type library partial extensions (non-static class, member type that is not `TypeIdentity`/`TypeReference`, unresolvable type/namespace, duplicate members, invalid class name, invalid namespace, invalid member accessibility, value members without an initializer, marker members without an explicit `= default`, a spec @@ -292,9 +341,10 @@ that is not declared `partial`, and a spec class whose name collides with the ge `TLB0012` when they share a namespace, `TLB0013` when they do not). It also reports `TLB0014` when a source partial class shares the generated library's name but is declared in a different namespace (so it will not merge), `TLB0015` when a same-namespace partial does not match the generated -`public static partial` modifiers, and `TLB0016`–`TLB0019` for invalid `[EnumValue]` members (member type, -a missing sibling enum declaration, and duplicate members/values). `TLB0002`, `TLB0008`, `TLB0010`, -`TLB0011`, `TLB0012`, and `TLB0013` have code fixes. +`public static partial` modifiers, `TLB0016`–`TLB0019` for invalid `[EnumValue]`/`[EnumValues]` members +(member type, a missing sibling enum declaration, a non-enum type, and duplicate members/values), and +`TLB0020` when an `[EnumValues]` marker references a type that is not an enum. `TLB0002`, `TLB0008`, +`TLB0010`, `TLB0011`, `TLB0012`, and `TLB0013` have code fixes. The generator carries these same diagnostics on its `GeneratorResult` and gates generation on `ShouldProcess`. Most are blocking (`IsBlocking: true`) and stop generation, but the non-blocking rules — diff --git a/global.json b/global.json index 5bb362b..f8c7bba 100644 --- a/global.json +++ b/global.json @@ -3,9 +3,9 @@ "allowPrerelease": false }, "msbuild-sdks": { - "Purview.BuildSdk": "1.0.0-prerelease.56" + "Purview.BuildSdk": "1.0.0-prerelease.57" }, "test": { "runner": "Microsoft.Testing.Platform" } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 25c5ca0..0ce81b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "purview-sourcegenerator-framework", - "version": "1.0.0-prerelease.45", + "version": "1.0.0-prerelease.46", "license": "MIT", "author": { "name": "Kieron Lanning", @@ -14,4 +14,4 @@ "type": "git", "url": "git+https://github.com/purview-dev/sourcegenerator-framework.git" } -} \ No newline at end of file +} diff --git a/src/src/SourceGeneratorFramework.Analyzers/AnalyzerReleases.Unshipped.md b/src/src/SourceGeneratorFramework.Analyzers/AnalyzerReleases.Unshipped.md index 4f0434a..8f89971 100644 --- a/src/src/SourceGeneratorFramework.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/src/SourceGeneratorFramework.Analyzers/AnalyzerReleases.Unshipped.md @@ -19,4 +19,5 @@ TLB0015 | TypeLibrary | Info | Type library partial extension must be declared ' TLB0016 | TypeLibrary | Error | Enum value member type must be TypeIdentity or EnumValueDefinition TLB0017 | TypeLibrary | Error | Enum value member references an enum type that is not declared TLB0018 | TypeLibrary | Error | Duplicate enum value member -TLB0019 | TypeLibrary | Info | Duplicate enum value \ No newline at end of file +TLB0019 | TypeLibrary | Info | Duplicate enum value +TLB0020 | TypeLibrary | Error | Enum values member references a type that is not an enum \ No newline at end of file diff --git a/src/src/SourceGeneratorFramework.Analyzers/AttributeDataModelValidationAnalyzer.cs b/src/src/SourceGeneratorFramework.Analyzers/AttributeDataModelValidationAnalyzer.cs index 4337e0d..b47a4ef 100644 --- a/src/src/SourceGeneratorFramework.Analyzers/AttributeDataModelValidationAnalyzer.cs +++ b/src/src/SourceGeneratorFramework.Analyzers/AttributeDataModelValidationAnalyzer.cs @@ -245,7 +245,7 @@ static ImmutableDictionary< > CollectTypeLibrarySpecData(Compilation compilation, INamedTypeSymbol? generateTypeLibraryAttributeType) { if (generateTypeLibraryAttributeType is null) - return ImmutableDictionary>.Empty; + return []; var typeRefAttributeType = compilation.GetTypeByMetadataName( "Purview.SourceGeneratorFramework.Generators.TypeRefAttribute" @@ -312,6 +312,7 @@ void VisitType(INamedTypeSymbol type) : typeSymbol.ContainingNamespace.ToDisplayString(); } + // If the constructor is not the namespace-only form, the namespace is the second argument. return GetStringCtorArgument(typeRef, 1); } @@ -320,6 +321,7 @@ void VisitType(INamedTypeSymbol type) if (index < 0 || index >= attributeData.ConstructorArguments.Length) return null; + // If the constructor argument is a string, return it directly. return attributeData.ConstructorArguments[index].Value as string; } @@ -340,9 +342,10 @@ out ITypeSymbol? targetType { targetType = null; - var attributeSyntax = - generateAttribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken) as AttributeSyntax; - if (attributeSyntax is null) + if ( + generateAttribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken) + is not AttributeSyntax attributeSyntax + ) return false; var expression = attributeSyntax.ArgumentList?.Arguments.FirstOrDefault()?.Expression; diff --git a/src/src/SourceGeneratorFramework.Analyzers/TypeLibraryValidationAnalyzer.cs b/src/src/SourceGeneratorFramework.Analyzers/TypeLibraryValidationAnalyzer.cs index 27bbea2..8720451 100644 --- a/src/src/SourceGeneratorFramework.Analyzers/TypeLibraryValidationAnalyzer.cs +++ b/src/src/SourceGeneratorFramework.Analyzers/TypeLibraryValidationAnalyzer.cs @@ -60,6 +60,8 @@ public sealed class TypeLibraryValidationAnalyzer : DiagnosticAnalyzer public static DiagnosticDescriptor EnumValueDuplicateValue => TypeLibraryDiagnosticRules.EnumValueDuplicateValue; + public static DiagnosticDescriptor EnumValuesTypeNotEnum => TypeLibraryDiagnosticRules.EnumValuesTypeNotEnum; + public override ImmutableArray SupportedDiagnostics => [ SpecNotStaticClass, @@ -80,6 +82,7 @@ public sealed class TypeLibraryValidationAnalyzer : DiagnosticAnalyzer EnumValueEnumTypeNotDeclared, EnumValueDuplicateMember, EnumValueDuplicateValue, + EnumValuesTypeNotEnum, ]; public override void Initialize(AnalysisContext context) @@ -107,6 +110,9 @@ public override void Initialize(AnalysisContext context) var enumValueAttributeType = context.Compilation.GetTypeByMetadataName( "Purview.SourceGeneratorFramework.Generators.EnumValueAttribute" ); + var enumValuesAttributeType = context.Compilation.GetTypeByMetadataName( + "Purview.SourceGeneratorFramework.Generators.EnumValuesAttribute" + ); var enumValueDefinitionType = context.Compilation.GetTypeByMetadataName( "Purview.SourceGeneratorFramework.EnumValueDefinition" ); @@ -127,6 +133,7 @@ public override void Initialize(AnalysisContext context) typeIdentityType, typeReferenceType, enumValueAttributeType, + enumValuesAttributeType, enumValueDefinitionType, generatedTypeLibraries ), @@ -142,6 +149,7 @@ static void AnalyzeNamedType( INamedTypeSymbol? typeIdentityType, INamedTypeSymbol? typeReferenceType, INamedTypeSymbol? enumValueAttributeType, + INamedTypeSymbol? enumValuesAttributeType, INamedTypeSymbol? enumValueDefinitionType, IReadOnlyList generatedTypeLibraries ) @@ -225,6 +233,7 @@ IReadOnlyList generatedTypeLibraries typeIdentityType, typeReferenceType, enumValueAttributeType, + enumValuesAttributeType, enumValueDefinitionType, memberNamesByPath, enumMemberNamesByGroup, @@ -242,6 +251,7 @@ static void AnalyzeMember( INamedTypeSymbol? typeIdentityType, INamedTypeSymbol? typeReferenceType, INamedTypeSymbol? enumValueAttributeType, + INamedTypeSymbol? enumValuesAttributeType, INamedTypeSymbol? enumValueDefinitionType, Dictionary> memberNamesByPath, Dictionary> enumMemberNamesByGroup, @@ -250,26 +260,71 @@ static void AnalyzeMember( Location typeLocation ) { - var enumValue = GetAttribute(field, enumValueAttributeType); - if (enumValue is not null) + var typeRef = GetAttribute(field, typeRefAttributeType); + var enumValueAttributes = GetAttributes(field, enumValueAttributeType); + var enumValuesAttribute = GetAttribute(field, enumValuesAttributeType); + + if (typeRef is null && enumValueAttributes.Count == 0 && enumValuesAttribute is null) + return; + + // A field can declare both [TypeRef] and enum-value markers; validate the [TypeRef] when present. + if (typeRef is not null) + { + AnalyzeTypeRefMember( + context, + field, + typeRef, + typeIdentityType, + typeReferenceType, + memberNamesByPath, + typeLocation + ); + } + + foreach (var enumValue in enumValueAttributes) { AnalyzeEnumValueMember( context, field, enumValue, + typeRefAttributeType, enumValueDefinitionType, enumMemberNamesByGroup, enumValuesByGroup, typeRefMarkersByPath, typeLocation ); - return; } - var typeRef = GetAttribute(field, typeRefAttributeType); - if (typeRef is null) - return; + if (enumValuesAttribute is not null) + { + AnalyzeEnumValuesMember( + context, + field, + enumValuesAttribute, + enumValueDefinitionType, + enumMemberNamesByGroup, + enumValuesByGroup, + typeRefMarkersByPath, + typeLocation + ); + } + } + /// + /// Validates a [TypeRef] member: member type, accessibility, and (for markers) a resolvable + /// type and namespace. + /// + static void AnalyzeTypeRefMember( + SymbolAnalysisContext context, + IFieldSymbol field, + AttributeData typeRef, + INamedTypeSymbol? typeIdentityType, + INamedTypeSymbol? typeReferenceType, + Dictionary> memberNamesByPath, + Location typeLocation + ) + { var memberLocation = field.Locations.FirstOrDefault(static location => location.IsInSource) ?? typeLocation; var isTypeIdentity = @@ -339,12 +394,15 @@ Location typeLocation /// /// Validates an [EnumValue] marker field: member type, accessibility, the referenced enum - /// type, and duplicate detection within the enum's value group. + /// type, and duplicate detection within the enum's value group. When the field also declares a + /// [TypeRef], the enum type is inferred from it and the attribute's first argument is the + /// enum member name. /// static void AnalyzeEnumValueMember( SymbolAnalysisContext context, IFieldSymbol field, AttributeData enumValue, + INamedTypeSymbol? typeRefAttributeType, INamedTypeSymbol? enumValueDefinitionType, Dictionary> enumMemberNamesByGroup, Dictionary> enumValuesByGroup, @@ -377,8 +435,8 @@ enumValueDefinitionType is not null if (HasNoInitializer(field, context.CancellationToken)) context.ReportDiagnostic(Diagnostic.Create(MarkerMissingDefaultInitializer, memberLocation, field.Name)); - var (enumName, enumNamespace, value) = ReadEnumValue(enumValue); - if (enumName is null) + var (declaredName, declaredNamespace, value) = ReadEnumValue(enumValue); + if (declaredName is null) { context.ReportDiagnostic( Diagnostic.Create(EnumValueEnumTypeNotDeclared, memberLocation, field.Name, string.Empty, string.Empty) @@ -386,21 +444,38 @@ enumValueDefinitionType is not null return; } - if (!typeRefMarkersByPath.Contains((enumNamespace ?? string.Empty) + "\0" + enumName)) + var typeRef = typeRefAttributeType is null ? null : GetAttribute(field, typeRefAttributeType); + + string enumName; + string enumNamespace; + string memberName; + + if (typeRef is not null) + { + // The enum type comes from the sibling [TypeRef]; the first attribute argument is the member name. + if (!TryResolveTypeRef(typeRef, field.Name, out var typeName, out var memberNamespace) || typeName is null) + return; + + memberName = declaredName; + enumName = typeName; + enumNamespace = memberNamespace ?? string.Empty; + } + else + { + memberName = field.Name; + enumName = declaredName; + enumNamespace = declaredNamespace ?? string.Empty; + } + + if (!typeRefMarkersByPath.Contains(enumNamespace + "\0" + enumName)) { context.ReportDiagnostic( - Diagnostic.Create( - EnumValueEnumTypeNotDeclared, - memberLocation, - field.Name, - enumName, - enumNamespace ?? string.Empty - ) + Diagnostic.Create(EnumValueEnumTypeNotDeclared, memberLocation, field.Name, enumName, enumNamespace) ); return; } - var groupKey = (enumNamespace ?? string.Empty) + "\0" + enumName; + var groupKey = enumNamespace + "\0" + enumName; if (!enumMemberNamesByGroup.TryGetValue(groupKey, out var memberNames)) { @@ -408,7 +483,7 @@ enumValueDefinitionType is not null enumMemberNamesByGroup[groupKey] = memberNames; } - if (!memberNames.Add(field.Name)) + if (!memberNames.Add(memberName)) { context.ReportDiagnostic(Diagnostic.Create(EnumValueDuplicateMember, memberLocation, field.Name, enumName)); return; @@ -416,7 +491,7 @@ enumValueDefinitionType is not null if (!enumValuesByGroup.TryGetValue(groupKey, out var values)) { - values = new(); + values = []; enumValuesByGroup[groupKey] = values; } @@ -424,6 +499,138 @@ enumValueDefinitionType is not null context.ReportDiagnostic(Diagnostic.Create(EnumValueDuplicateValue, memberLocation, field.Name, enumName)); } + /// + /// Validates an [EnumValues] marker field: member type, accessibility, the referenced enum + /// type, and duplicate detection against every value in the enum's value group. + /// + static void AnalyzeEnumValuesMember( + SymbolAnalysisContext context, + IFieldSymbol field, + AttributeData enumValues, + INamedTypeSymbol? enumValueDefinitionType, + Dictionary> enumMemberNamesByGroup, + Dictionary> enumValuesByGroup, + HashSet typeRefMarkersByPath, + Location typeLocation + ) + { + var memberLocation = field.Locations.FirstOrDefault(static location => location.IsInSource) ?? typeLocation; + + var isTypeIdentity = + field.Type.Name == "TypeIdentity" + || ( + enumValueDefinitionType is not null + && SymbolEqualityComparer.Default.Equals(field.Type, enumValueDefinitionType) + ); + if (!isTypeIdentity) + { + context.ReportDiagnostic( + Diagnostic.Create(EnumValueMemberTypeInvalid, memberLocation, field.Name, field.Type) + ); + return; + } + + if (field.DeclaredAccessibility != Accessibility.Private) + { + context.ReportDiagnostic(Diagnostic.Create(MemberAccessibilityInvalid, memberLocation, field.Name)); + return; + } + + if (HasNoInitializer(field, context.CancellationToken)) + context.ReportDiagnostic(Diagnostic.Create(MarkerMissingDefaultInitializer, memberLocation, field.Name)); + + var enumType = ReadEnumValuesType(enumValues); + if (enumType is null || enumType.TypeKind != TypeKind.Enum) + { + context.ReportDiagnostic( + Diagnostic.Create( + EnumValuesTypeNotEnum, + memberLocation, + field.Name, + enumType?.ToDisplayString() ?? string.Empty + ) + ); + return; + } + + var enumName = enumType.Name; + var enumNamespace = enumType.ContainingNamespace.IsGlobalNamespace + ? string.Empty + : enumType.ContainingNamespace.ToDisplayString(); + + if (!typeRefMarkersByPath.Contains(enumNamespace + "\0" + enumName)) + { + context.ReportDiagnostic( + Diagnostic.Create(EnumValueEnumTypeNotDeclared, memberLocation, field.Name, enumName, enumNamespace) + ); + return; + } + + var groupKey = enumNamespace + "\0" + enumName; + if (!enumMemberNamesByGroup.TryGetValue(groupKey, out var memberNames)) + { + memberNames = new(StringComparer.Ordinal); + enumMemberNamesByGroup[groupKey] = memberNames; + } + + if (!enumValuesByGroup.TryGetValue(groupKey, out var values)) + { + values = []; + enumValuesByGroup[groupKey] = values; + } + + foreach (var enumMember in enumType.GetMembers().OfType()) + { + if (!enumMember.HasConstantValue) + continue; + + if (!memberNames.Add(enumMember.Name)) + { + context.ReportDiagnostic( + Diagnostic.Create(EnumValueDuplicateMember, memberLocation, field.Name, enumMember.Name, enumName) + ); + continue; + } + + if (!values.Add(ReadEnumValueConstant(enumMember.ConstantValue))) + { + context.ReportDiagnostic( + Diagnostic.Create(EnumValueDuplicateValue, memberLocation, field.Name, enumMember.Name, enumName) + ); + } + } + } + + /// + /// Reads an [EnumValues] attribute, resolving the enum type from its typeof(...) + /// constructor argument. + /// + static INamedTypeSymbol? ReadEnumValuesType(AttributeData enumValues) + { + if (enumValues.ConstructorArguments.Length == 0) + return null; + + return enumValues.ConstructorArguments[0].Value as INamedTypeSymbol; + } + + /// + /// Reads an enum member's constant value, normalizing it to a so every enum + /// underlying type (byte through ulong) is compared exactly. + /// + static decimal ReadEnumValueConstant(object? constantValue) => + constantValue switch + { + byte b => b, + sbyte sb => sb, + short s => s, + ushort us => us, + int i => i, + uint ui => ui, + long l => l, + ulong ul => ul, + _ => Convert.ToDecimal(constantValue, System.Globalization.CultureInfo.InvariantCulture), + }; + /// /// Reads an [EnumValue] attribute, supporting the explicit enum-name/namespace form and the /// single fully-qualified enum type name form. The value is normalized to a so @@ -465,6 +672,7 @@ static decimal ReadEnumValueConstant(AttributeData attributeData, int index) if (index < 0 || index >= attributeData.ConstructorArguments.Length) return 0; + // The value is boxed as the underlying type of the enum, so we switch on the known numeric types and return attributeData.ConstructorArguments[index].Value switch { byte b => b, @@ -788,6 +996,24 @@ attribute.AttributeClass is not null return null; } + static List GetAttributes(ISymbol symbol, INamedTypeSymbol? attributeType) + { + List result = []; + if (attributeType is null) + return result; + + foreach (var attribute in symbol.GetAttributes()) + { + if ( + attribute.AttributeClass is not null + && SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeType) + ) + result.Add(attribute); + } + + return result; + } + static T? GetConstructorArgument(AttributeData attributeData, int index, T? defaultValue) { if (index < 0 || index >= attributeData.ConstructorArguments.Length) @@ -839,19 +1065,12 @@ attribute.AttributeClass is not null /// Describes the shape of the type library class a [GenerateTypeLibrary] spec generates, so /// the analyzer can detect source partial declarations that would not merge with it. /// - sealed class GeneratedTypeLibraryInfo + sealed class GeneratedTypeLibraryInfo(string generatedName, string? generatedNamespace, INamedTypeSymbol spec) { - public GeneratedTypeLibraryInfo(string generatedName, string? generatedNamespace, INamedTypeSymbol spec) - { - GeneratedName = generatedName; - GeneratedNamespace = generatedNamespace; - Spec = spec; - } - - public string GeneratedName { get; } + public string GeneratedName { get; } = generatedName; - public string? GeneratedNamespace { get; } + public string? GeneratedNamespace { get; } = generatedNamespace; - public INamedTypeSymbol Spec { get; } + public INamedTypeSymbol Spec { get; } = spec; } } diff --git a/src/src/SourceGeneratorFramework.Benchmarks/SourceGeneratorFramework.Benchmarks.csproj b/src/src/SourceGeneratorFramework.Benchmarks/SourceGeneratorFramework.Benchmarks.csproj index c77cfe3..a74160b 100644 --- a/src/src/SourceGeneratorFramework.Benchmarks/SourceGeneratorFramework.Benchmarks.csproj +++ b/src/src/SourceGeneratorFramework.Benchmarks/SourceGeneratorFramework.Benchmarks.csproj @@ -8,6 +8,7 @@ + diff --git a/src/src/SourceGeneratorFramework.ExampleGenerator/CodeWriterSampleEmitter.cs b/src/src/SourceGeneratorFramework.ExampleGenerator/CodeWriterSampleEmitter.cs index f4c472c..da5139f 100644 --- a/src/src/SourceGeneratorFramework.ExampleGenerator/CodeWriterSampleEmitter.cs +++ b/src/src/SourceGeneratorFramework.ExampleGenerator/CodeWriterSampleEmitter.cs @@ -162,7 +162,7 @@ options with Parameters = [ new("source", TypeIdentity.Create().AsTypeReference()), - new("onBuilt", PurviewTypeLibrary.System.Action.AsTypeReference()), + new("onBuilt", PurviewTypeLibrary.System.Action.WithArity(0).AsTypeReference()), ], }, methodBody => diff --git a/src/src/SourceGeneratorFramework.ExampleGenerator/SeverityAttributes.cs b/src/src/SourceGeneratorFramework.ExampleGenerator/SeverityAttributes.cs index d8c07f5..11d1b5f 100644 --- a/src/src/SourceGeneratorFramework.ExampleGenerator/SeverityAttributes.cs +++ b/src/src/SourceGeneratorFramework.ExampleGenerator/SeverityAttributes.cs @@ -35,22 +35,17 @@ public enum SeverityLevel /// names ("Inherit") and the generator expands them to the fully-qualified enum member name derived from /// the resolved target attribute. /// -[global::System.AttributeUsage(global::System.AttributeTargets.All, AllowMultiple = false)] -public sealed class SeverityAttribute : global::System.Attribute +/// +/// Initializes a new instance of the class. +/// +/// The severity level. +[AttributeUsage(AttributeTargets.All, AllowMultiple = false)] +public sealed class SeverityAttribute(SeverityLevel severity) : Attribute { - /// - /// Initializes a new instance of the class. - /// - /// The severity level. - public SeverityAttribute(SeverityLevel severity) - { - Severity = severity; - } - /// /// Gets or sets the severity level. /// - public SeverityLevel Severity { get; set; } + public SeverityLevel Severity { get; } = severity; /// /// Gets or sets the severity level read from the named Level argument. diff --git a/src/src/SourceGeneratorFramework.ExampleGenerator/TypeLibrarySpec.cs b/src/src/SourceGeneratorFramework.ExampleGenerator/TypeLibrarySpec.cs index 49d8ee3..739107d 100644 --- a/src/src/SourceGeneratorFramework.ExampleGenerator/TypeLibrarySpec.cs +++ b/src/src/SourceGeneratorFramework.ExampleGenerator/TypeLibrarySpec.cs @@ -18,7 +18,7 @@ static partial class TypeLibrarySpec /// /// The marker attribute, declared with GenerateFullNameConst so the /// generated SeverityAttributeFullName constant can be used as the target of the - /// attribute-data model. + /// attribute-data model. /// [TypeRef("Purview.SourceGeneratorFramework.Examples", GenerateFullNameConst = true)] static readonly TypeIdentity SeverityAttribute = default; @@ -65,26 +65,13 @@ static partial class TypeLibrarySpec /// /// The ServiceLifetime enum type, declared with GenerateFullNameConst so the generated - /// ServiceLifetimeFullName constant and the per-value full-name constants are available. + /// ServiceLifetimeFullName constant and the per-value full-name constants are available. The + /// enum values are declared inline on the same field: each [EnumValue] names the enum member + /// and the enum type is inferred from the sibling [TypeRef]. /// [TypeRef("ServiceLifetime", "Purview.SourceGeneratorFramework.Examples", GenerateFullNameConst = true)] + [EnumValue("Singleton", 0)] + [EnumValue("Scoped", 1)] + [EnumValue("Transient", 2)] static readonly TypeIdentity ServiceLifetime = default; - - /// - /// The ServiceLifetime.Singleton enum value, declared with the single full type name form. - /// - [EnumValue("Purview.SourceGeneratorFramework.Examples.ServiceLifetime", 0)] - static readonly TypeIdentity Singleton = default; - - /// - /// The ServiceLifetime.Scoped enum value, declared with the explicit enum-name/namespace form. - /// - [EnumValue("ServiceLifetime", "Purview.SourceGeneratorFramework.Examples", 1)] - static readonly TypeIdentity Scoped = default; - - /// - /// The ServiceLifetime.Transient enum value. - /// - [EnumValue("ServiceLifetime", "Purview.SourceGeneratorFramework.Examples", 2)] - static readonly TypeIdentity Transient = default; } diff --git a/src/src/SourceGeneratorFramework.Generators/Helpers/AttributeDataModelLibrary.cs b/src/src/SourceGeneratorFramework.Generators/Helpers/AttributeDataModelLibrary.cs index 47ba152..e084c07 100644 --- a/src/src/SourceGeneratorFramework.Generators/Helpers/AttributeDataModelLibrary.cs +++ b/src/src/SourceGeneratorFramework.Generators/Helpers/AttributeDataModelLibrary.cs @@ -1047,6 +1047,7 @@ static TypeLibrarySpecData ReadTypeLibrarySpecData(INamedTypeSymbol specSymbol) : typeSymbol.ContainingNamespace.ToDisplayString(); } + // The typeof(...) form is the only remaining valid form, which has a single constructor argument of type return GetConstructorArgument(typeRef, 1, (string?)null); } @@ -1077,9 +1078,10 @@ out TypeIdentity targetIdentity targetType = null; targetIdentity = default; - var attributeSyntax = - generateAttribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken) as AttributeSyntax; - if (attributeSyntax is null) + if ( + generateAttribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken) + is not AttributeSyntax attributeSyntax + ) return false; var expression = attributeSyntax.ArgumentList?.Arguments.FirstOrDefault()?.Expression; diff --git a/src/src/SourceGeneratorFramework.Generators/Helpers/GeneratorTypeLibrary.cs b/src/src/SourceGeneratorFramework.Generators/Helpers/GeneratorTypeLibrary.cs index 4e72e4c..3b4bd7f 100644 --- a/src/src/SourceGeneratorFramework.Generators/Helpers/GeneratorTypeLibrary.cs +++ b/src/src/SourceGeneratorFramework.Generators/Helpers/GeneratorTypeLibrary.cs @@ -38,5 +38,7 @@ public static class Attirbutes public static readonly TypeIdentity TypeRefAttribute = new(nameof(TypeRefAttribute), GeneratorsNamespace); public static readonly TypeIdentity EnumValueAttribute = new(nameof(EnumValueAttribute), GeneratorsNamespace); + + public static readonly TypeIdentity EnumValuesAttribute = new(nameof(EnumValuesAttribute), GeneratorsNamespace); } } diff --git a/src/src/SourceGeneratorFramework.Generators/Helpers/SourceEmitter.TypeLibrary.cs b/src/src/SourceGeneratorFramework.Generators/Helpers/SourceEmitter.TypeLibrary.cs index 1272127..8352e8c 100644 --- a/src/src/SourceGeneratorFramework.Generators/Helpers/SourceEmitter.TypeLibrary.cs +++ b/src/src/SourceGeneratorFramework.Generators/Helpers/SourceEmitter.TypeLibrary.cs @@ -210,7 +210,9 @@ static SourceText EnumValueAttribute() var writer = CreateTypeLibraryWriter(GeneratorTypeLibrary.Attirbutes.EnumValueAttribute); return writer - .XmlSummary("Declares an enum value member for a generated type library.") + .XmlSummary( + "Declares an enum value member for a generated type library. May be declared multiple times on a single field." + ) .AttributeClass( new(GeneratorTypeLibrary.Attirbutes.EnumValueAttribute), AttributeTargets.Field, @@ -251,10 +253,12 @@ static SourceText EnumValueAttribute() ); bodyWriter - .XmlSummary("Declares an enum value by the enum type's fully qualified name.") + .XmlSummary( + "Declares an enum value by the enum type's fully qualified name, or by the member name when applied on a [TypeRef] field." + ) .XmlParam( "enumFullName", - "The fully qualified name of the enum type, such as 'Namespace.EnumName'." + "The fully qualified name of the enum type, such as 'Namespace.EnumName'. When this attribute is applied on a field that also carries [TypeRef], this is the enum member name and the enum type is inferred from the [TypeRef]." ) .XmlParam( "value", @@ -318,6 +322,50 @@ static SourceText EnumValueAttribute() bodyWriter .XmlSummary("Gets the optional alternate names used when matching.") .Property(new("Aliases", StringArrayReference.Nullable(), TypeDeclarationAccessibility.Public)); + }, + allowMultiple: true + ); + } + + static SourceText EnumValuesAttribute() + { + var writer = CreateTypeLibraryWriter(GeneratorTypeLibrary.Attirbutes.EnumValuesAttribute); + + return writer + .XmlSummary("Declares all enum values of a compile-time-available enum type for a generated type library.") + .AttributeClass( + new(GeneratorTypeLibrary.Attirbutes.EnumValuesAttribute), + AttributeTargets.Field, + bodyWriter => + { + bodyWriter + .XmlSummary( + "Declares every member of the specified enum type. The enum type must also be declared by a sibling [TypeRef] member in the same namespace." + ) + .XmlParam( + "enumType", + "The enum type, supplied as a typeof(...) value; its members become the generated enum values." + ) + .Constructor( + new( + GeneratorTypeLibrary.Attirbutes.EnumValuesAttribute, + TypeDeclarationAccessibility.Public + ) + { + Parameters = [new("enumType", PurviewTypeLibrary.System.Type)], + }, + constructorWriter => constructorWriter.Assignment("EnumType", "enumType") + ); + + bodyWriter + .XmlSummary("Gets the enum type whose members are declared.") + .Property( + new( + "EnumType", + PurviewTypeLibrary.System.Type.MakeNullable(), + TypeDeclarationAccessibility.Public + ) + ); } ); } @@ -644,8 +692,11 @@ static IEnumerable GetTypeRefMarkerNames(TypeLibraryNamespaceNode node) foreach (var group in node.EnumGroups) { - foreach (var value in group.Values) - yield return value.MemberName; + // Each spec marker field that contributed to the group must be referenced so the compiler's + // unused-member analysis does not flag it. Enum member names generated from an [EnumValues] + // marker are not spec fields and must not be referenced. + foreach (var marker in group.MarkerFieldNames) + yield return marker; } foreach (var child in node.Children) diff --git a/src/src/SourceGeneratorFramework.Generators/Helpers/SourceEmitter.cs b/src/src/SourceGeneratorFramework.Generators/Helpers/SourceEmitter.cs index 81972d9..f6e83d6 100644 --- a/src/src/SourceGeneratorFramework.Generators/Helpers/SourceEmitter.cs +++ b/src/src/SourceGeneratorFramework.Generators/Helpers/SourceEmitter.cs @@ -19,6 +19,7 @@ static partial class SourceEmitter yield return (GetHintName(nameof(GenerateTypeLibraryAttribute)), GenerateTypeLibraryAttribute()); yield return (GetHintName(nameof(TypeRefAttribute)), TypeRefAttribute()); yield return (GetHintName(nameof(EnumValueAttribute)), EnumValueAttribute()); + yield return (GetHintName(nameof(EnumValuesAttribute)), EnumValuesAttribute()); } static CodeWriter CreateWriter(TypeReference type) diff --git a/src/src/SourceGeneratorFramework.Generators/Helpers/TypeLibraryModelLibrary.cs b/src/src/SourceGeneratorFramework.Generators/Helpers/TypeLibraryModelLibrary.cs index 5ebfd47..d865e29 100644 --- a/src/src/SourceGeneratorFramework.Generators/Helpers/TypeLibraryModelLibrary.cs +++ b/src/src/SourceGeneratorFramework.Generators/Helpers/TypeLibraryModelLibrary.cs @@ -56,177 +56,17 @@ CancellationToken cancellationToken AddFrameworkTree(frameworkTree, root, nodeLookup); foreach (var field in specSymbol.GetMembers().OfType()) - { - cancellationToken.ThrowIfCancellationRequested(); - - var enumValue = GetAttribute(field, GeneratorTypeLibrary.Attirbutes.EnumValueAttribute); - if (enumValue is not null) - continue; - - var typeRef = GetAttribute(field, GeneratorTypeLibrary.Attirbutes.TypeRefAttribute); - if (typeRef is null) - continue; + ProcessTypeRefField(field, root, nodeLookup, diagnostics, cancellationToken); - var isTypeIdentity = string.Equals(field.Type.Name, "TypeIdentity", StringComparison.Ordinal); - var isTypeReference = string.Equals(field.Type.Name, "TypeReference", StringComparison.Ordinal); - var documentation = ExtractDocumentation(field); - var includeInGetTypes = GetIncludeInGetTypes(typeRef); - var generateFullNameConstant = GetGenerateFullNameConstant(typeRef); - - TypeLibraryMemberModel member; - - if (isTypeReference) - { - var initializer = ReadInitializerExpression(field, cancellationToken); - if (initializer is null) - { - diagnostics.Add( - ReportableDiagnostic.Create( - TypeLibraryDiagnosticRules.ReferenceMemberMissingInitializer, - isBlocking: true, - field, - field.Name - ) - ); - continue; - } - - if (field.DeclaredAccessibility != Accessibility.Internal) - { - diagnostics.Add( - ReportableDiagnostic.Create( - TypeLibraryDiagnosticRules.MemberAccessibilityInvalid, - isBlocking: true, - field, - field.Name - ) - ); - continue; - } - - var placementNamespace = GetPlacementNamespace(typeRef); - member = new( - field.Name, - null, - null, - 0, - initializer, - IsTypeReference: true, - documentation, - includeInGetTypes, - generateFullNameConstant - ); - AddToTree(root, nodeLookup, placementNamespace ?? string.Empty, member); - } - else if (isTypeIdentity && HasRealInitializer(field, cancellationToken)) - { - // An initialised TypeIdentity follows the same rules as a TypeReference value member. - var initializer = ReadInitializerExpression(field, cancellationToken); - if (initializer is null) - { - diagnostics.Add( - ReportableDiagnostic.Create( - TypeLibraryDiagnosticRules.ReferenceMemberMissingInitializer, - isBlocking: true, - field, - field.Name - ) - ); - continue; - } - - if (field.DeclaredAccessibility != Accessibility.Internal) - { - diagnostics.Add( - ReportableDiagnostic.Create( - TypeLibraryDiagnosticRules.MemberAccessibilityInvalid, - isBlocking: true, - field, - field.Name - ) - ); - continue; - } - - var placementNamespace = GetPlacementNamespace(typeRef); - member = new( - field.Name, - null, - null, - 0, - initializer, - IsTypeReference: false, - documentation, - includeInGetTypes, - generateFullNameConstant - ); - AddToTree(root, nodeLookup, placementNamespace ?? string.Empty, member); - } - else if (isTypeIdentity) - { - // A plain TypeIdentity is a generation marker: private accessibility. - if (field.DeclaredAccessibility != Accessibility.Private) - { - diagnostics.Add( - ReportableDiagnostic.Create( - TypeLibraryDiagnosticRules.MemberAccessibilityInvalid, - isBlocking: true, - field, - field.Name - ) - ); - continue; - } - - if (HasNoInitializer(field, cancellationToken)) - { - diagnostics.Add( - ReportableDiagnostic.Create( - TypeLibraryDiagnosticRules.MarkerMissingDefaultInitializer, - isBlocking: false, - field, - field.Name - ) - ); - } - - var before = diagnostics.Count; - var (typeName, memberNamespace, genericArity) = ReadTypeRef(typeRef, field, diagnostics); - if (diagnostics.Count > before) - continue; + if (diagnostics.Any(d => d.IsBlocking)) + return GeneratorResult.Create([.. diagnostics]); - member = new( - field.Name, - typeName, - memberNamespace, - genericArity, - null, - IsTypeReference: false, - documentation, - includeInGetTypes, - generateFullNameConstant - ); - AddToTree(root, nodeLookup, memberNamespace, member); - } - else - { - diagnostics.Add( - ReportableDiagnostic.Create( - TypeLibraryDiagnosticRules.MemberTypeInvalid, - isBlocking: true, - field, - field.Name, - field.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) - ) - ); - continue; - } - } + ProcessEnumValueMembers(specSymbol, nodeLookup, diagnostics, cancellationToken); if (diagnostics.Any(d => d.IsBlocking)) return GeneratorResult.Create([.. diagnostics]); - ProcessEnumValueMembers(specSymbol, nodeLookup, diagnostics, cancellationToken); + ProcessEnumValuesMembers(specSymbol, nodeLookup, diagnostics, cancellationToken); if (diagnostics.Any(d => d.IsBlocking)) return GeneratorResult.Create([.. diagnostics]); @@ -254,6 +94,137 @@ .. root.OrderBy(static n => n.Name, StringComparer.Ordinal).Select(static n => n ); } + /// + /// Processes a single [TypeRef] field, adding its generated member to the tree or reporting a + /// diagnostic. Fields that carry a [TypeRef] are processed here even when they also declare + /// [EnumValue]/[EnumValues] markers; fields without a [TypeRef] are handled by + /// the enum-value processing passes. + /// + static void ProcessTypeRefField( + IFieldSymbol field, + List root, + Dictionary nodeLookup, + List diagnostics, + CancellationToken cancellationToken + ) + { + var typeRef = GetAttribute(field, GeneratorTypeLibrary.Attirbutes.TypeRefAttribute); + if (typeRef is null) + return; + + var isTypeIdentity = string.Equals(field.Type.Name, "TypeIdentity", StringComparison.Ordinal); + var isTypeReference = string.Equals(field.Type.Name, "TypeReference", StringComparison.Ordinal); + var documentation = ExtractDocumentation(field); + var includeInGetTypes = GetIncludeInGetTypes(typeRef); + var generateFullNameConstant = GetGenerateFullNameConstant(typeRef); + + TypeLibraryMemberModel member; + + if (isTypeReference || (isTypeIdentity && HasRealInitializer(field, cancellationToken))) + { + // A TypeReference, or an initialised TypeIdentity, follows the value-member rules: it + // requires a value and internal accessibility. + var initializer = ReadInitializerExpression(field, cancellationToken); + if (initializer is null) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.ReferenceMemberMissingInitializer, + isBlocking: true, + field, + field.Name + ) + ); + return; + } + + if (field.DeclaredAccessibility != Accessibility.Internal) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.MemberAccessibilityInvalid, + isBlocking: true, + field, + field.Name + ) + ); + return; + } + + var placementNamespace = GetPlacementNamespace(typeRef); + member = new( + field.Name, + null, + null, + 0, + initializer, + IsTypeReference: isTypeReference, + documentation, + includeInGetTypes, + generateFullNameConstant + ); + AddToTree(root, nodeLookup, placementNamespace ?? string.Empty, member); + } + else if (isTypeIdentity) + { + // A plain TypeIdentity is a generation marker: private accessibility. + if (field.DeclaredAccessibility != Accessibility.Private) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.MemberAccessibilityInvalid, + isBlocking: true, + field, + field.Name + ) + ); + return; + } + + if (HasNoInitializer(field, cancellationToken)) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.MarkerMissingDefaultInitializer, + isBlocking: false, + field, + field.Name + ) + ); + } + + var before = diagnostics.Count; + var (typeName, memberNamespace, genericArity) = ReadTypeRef(typeRef, field, diagnostics); + if (diagnostics.Count > before) + return; + + member = new( + field.Name, + typeName, + memberNamespace, + genericArity, + null, + IsTypeReference: false, + documentation, + includeInGetTypes, + generateFullNameConstant + ); + AddToTree(root, nodeLookup, memberNamespace, member); + } + else + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.MemberTypeInvalid, + isBlocking: true, + field, + field.Name, + field.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) + ) + ); + } + } + /// /// Resolves the placement namespace for a value member from the [TypeRef] namespace argument /// or named property. @@ -432,11 +403,12 @@ CancellationToken cancellationToken { cancellationToken.ThrowIfCancellationRequested(); - var enumValue = GetAttribute(field, GeneratorTypeLibrary.Attirbutes.EnumValueAttribute); - if (enumValue is null) + var enumValues = GetAttributes(field, GeneratorTypeLibrary.Attirbutes.EnumValueAttribute); + if (enumValues.Count == 0) continue; - ProcessEnumValue(field, enumValue, nodeLookup, diagnostics, cancellationToken); + foreach (var enumValue in enumValues) + ProcessEnumValue(field, enumValue, nodeLookup, diagnostics, cancellationToken); } } @@ -491,8 +463,12 @@ CancellationToken cancellationToken return; } - var (enumName, enumNamespace, value, underlyingType, aliases) = ReadEnumValue(enumValue); - if (enumName is null) + // When the field also declares a [TypeRef], the attribute's first argument is the enum member + // name and the enum type is inferred from the sibling [TypeRef] on the same field. + var typeRef = GetAttribute(field, GeneratorTypeLibrary.Attirbutes.TypeRefAttribute); + + var (declaredName, declaredNamespace, value, underlyingType, aliases) = ReadEnumValue(enumValue); + if (declaredName is null) { diagnostics.Add( ReportableDiagnostic.Create( @@ -507,7 +483,29 @@ CancellationToken cancellationToken return; } - if (!nodeLookup.TryGetValue(enumNamespace ?? string.Empty, out var leaf)) + string enumName; + string enumNamespace; + string memberName; + + if (typeRef is not null) + { + var before = diagnostics.Count; + var (typeName, memberNamespace, _) = ReadTypeRef(typeRef, field, diagnostics); + if (diagnostics.Count > before) + return; + + memberName = declaredName; + enumName = typeName; + enumNamespace = memberNamespace ?? string.Empty; + } + else + { + memberName = fieldName; + enumName = declaredName; + enumNamespace = declaredNamespace ?? string.Empty; + } + + if (!nodeLookup.TryGetValue(enumNamespace, out var leaf)) { diagnostics.Add( ReportableDiagnostic.Create( @@ -516,7 +514,7 @@ CancellationToken cancellationToken field, fieldName, enumName, - enumNamespace ?? string.Empty + enumNamespace ) ); return; @@ -533,7 +531,7 @@ CancellationToken cancellationToken field, fieldName, enumName, - enumNamespace ?? string.Empty + enumNamespace ) ); return; @@ -542,11 +540,11 @@ CancellationToken cancellationToken var group = leaf.EnumGroups.FirstOrDefault(g => g.EnumName == enumName); if (group is null) { - group = new EnumGroupBuilder(enumName, enumNamespace ?? string.Empty, enumMember.GenerateFullNameConstant); + group = new EnumGroupBuilder(enumName, enumNamespace, enumMember.GenerateFullNameConstant); leaf.EnumGroups.Add(group); } - if (group.Values.Any(v => v.MemberName == fieldName)) + if (group.Values.Any(v => v.MemberName == memberName)) { diagnostics.Add( ReportableDiagnostic.Create( @@ -574,9 +572,13 @@ CancellationToken cancellationToken } var documentation = ExtractDocumentation(field); + // The [TypeRef] field that carries the marker is already referenced through the node members; + // only standalone [EnumValue] marker fields need to be added to the marker reference list. + if (typeRef is null) + group.MarkerFieldNames.Add(fieldName); group.Values.Add( new TypeLibraryEnumValueModel( - fieldName, + memberName, value, underlyingType, aliases is null or { Length: 0 } @@ -587,6 +589,250 @@ CancellationToken cancellationToken ); } + /// + /// Processes an [EnumValues] marker field, resolving its enum type and attaching every member + /// of the enum to the matching {EnumName}Values group in the enum's namespace node. + /// + static void ProcessEnumValuesMembers( + INamedTypeSymbol specSymbol, + Dictionary nodeLookup, + List diagnostics, + CancellationToken cancellationToken + ) + { + foreach (var field in specSymbol.GetMembers().OfType()) + { + cancellationToken.ThrowIfCancellationRequested(); + + var enumValues = GetAttribute(field, GeneratorTypeLibrary.Attirbutes.EnumValuesAttribute); + if (enumValues is null) + continue; + + ProcessEnumValues(field, enumValues, nodeLookup, diagnostics, cancellationToken); + } + } + + static void ProcessEnumValues( + IFieldSymbol field, + AttributeData enumValues, + Dictionary nodeLookup, + List diagnostics, + CancellationToken cancellationToken + ) + { + var fieldName = field.Name; + + if (field.DeclaredAccessibility != Accessibility.Private) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.MemberAccessibilityInvalid, + isBlocking: true, + field, + fieldName + ) + ); + return; + } + + if (HasNoInitializer(field, cancellationToken)) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.MarkerMissingDefaultInitializer, + isBlocking: false, + field, + fieldName + ) + ); + } + + var isTypeIdentity = string.Equals(field.Type.Name, "TypeIdentity", StringComparison.Ordinal); + var isEnumValueDefinition = string.Equals(field.Type.Name, "EnumValueDefinition", StringComparison.Ordinal); + if (!isTypeIdentity && !isEnumValueDefinition) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.EnumValueMemberTypeInvalid, + isBlocking: true, + field, + fieldName, + field.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) + ) + ); + return; + } + + var enumType = ReadEnumValuesType(enumValues); + if (enumType is null || enumType.TypeKind != TypeKind.Enum) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.EnumValuesTypeNotEnum, + isBlocking: true, + field, + fieldName, + enumType?.ToDisplayString() ?? string.Empty + ) + ); + return; + } + + var enumName = enumType.Name; + var enumNamespace = enumType.ContainingNamespace.IsGlobalNamespace + ? string.Empty + : enumType.ContainingNamespace.ToDisplayString(); + + if (!nodeLookup.TryGetValue(enumNamespace, out var leaf)) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.EnumValueEnumTypeNotDeclared, + isBlocking: true, + field, + fieldName, + enumName, + enumNamespace + ) + ); + return; + } + + // The enum type must be declared by a sibling [TypeRef] TypeIdentity marker in the same node. + var enumMember = leaf.Members.FirstOrDefault(m => m.MemberName == enumName && !m.IsReference); + if (enumMember is null) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.EnumValueEnumTypeNotDeclared, + isBlocking: true, + field, + fieldName, + enumName, + enumNamespace + ) + ); + return; + } + + var group = leaf.EnumGroups.FirstOrDefault(g => g.EnumName == enumName); + if (group is null) + { + group = new EnumGroupBuilder(enumName, enumNamespace, enumMember.GenerateFullNameConstant); + leaf.EnumGroups.Add(group); + } + + if (group.BulkMarkerFieldName is not null) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.EnumValueDuplicateMember, + isBlocking: true, + field, + fieldName, + enumName + ) + ); + return; + } + + var underlyingType = EnumUnderlyingTypeFromSymbol(enumType); + foreach (var enumMemberSymbol in enumType.GetMembers().OfType()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!enumMemberSymbol.HasConstantValue) + continue; + + var memberName = enumMemberSymbol.Name; + if (group.Values.Any(v => v.MemberName == memberName)) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.EnumValueDuplicateMember, + isBlocking: true, + field, + fieldName, + enumName + ) + ); + continue; + } + + var value = ReadEnumConstant(enumMemberSymbol.ConstantValue); + if (group.Values.Any(v => v.Value == value)) + { + diagnostics.Add( + ReportableDiagnostic.Create( + TypeLibraryDiagnosticRules.EnumValueDuplicateValue, + isBlocking: false, + field, + fieldName, + enumName + ) + ); + } + + group.Values.Add( + new TypeLibraryEnumValueModel( + memberName, + value, + underlyingType, + EquatableArray.Empty, + ExtractDocumentation(enumMemberSymbol) + ) + ); + } + + group.BulkMarkerFieldName = fieldName; + group.MarkerFieldNames.Add(fieldName); + } + + /// + /// Resolves the enum type from an [EnumValues(typeof(...))] constructor argument. + /// + static INamedTypeSymbol? ReadEnumValuesType(AttributeData enumValues) + { + if (enumValues.ConstructorArguments.Length == 0) + return null; + + return enumValues.ConstructorArguments[0].Value as INamedTypeSymbol; + } + + /// + /// Maps an enum symbol's underlying type to the model value. + /// + static EnumUnderlyingType EnumUnderlyingTypeFromSymbol(INamedTypeSymbol enumType) => + enumType.EnumUnderlyingType?.SpecialType switch + { + SpecialType.System_Byte => EnumUnderlyingType.Byte, + SpecialType.System_SByte => EnumUnderlyingType.SByte, + SpecialType.System_Int16 => EnumUnderlyingType.Int16, + SpecialType.System_UInt16 => EnumUnderlyingType.UInt16, + SpecialType.System_Int32 => EnumUnderlyingType.Int32, + SpecialType.System_UInt32 => EnumUnderlyingType.UInt32, + SpecialType.System_Int64 => EnumUnderlyingType.Int64, + SpecialType.System_UInt64 => EnumUnderlyingType.UInt64, + _ => EnumUnderlyingType.Int32, + }; + + /// + /// Reads an enum member's constant value as a , preserving every underlying type. + /// + static decimal ReadEnumConstant(object? constantValue) => + constantValue switch + { + byte b => b, + sbyte sb => sb, + short s => s, + ushort us => us, + int i => i, + uint ui => ui, + long l => l, + ulong ul => ul, + _ => Convert.ToDecimal(constantValue, CultureInfo.InvariantCulture), + }; + /// /// Reads an [EnumValue] attribute, supporting the explicit enum-name/namespace form and the /// single fully-qualified enum type name form. Returns names for an empty or @@ -971,6 +1217,18 @@ sealed class PurviewTypeLibraryReferenceRewriter : CSharpSyntaxRewriter return null; } + static List GetAttributes(ISymbol symbol, TypeIdentity attributeType) + { + List result = []; + foreach (var attribute in symbol.GetAttributes()) + { + if (attribute.AttributeClass is not null && attributeType.Equals(attribute.AttributeClass)) + result.Add(attribute); + } + + return result; + } + static T? GetConstructorArgument(AttributeData attributeData, int index, T? defaultValue) { if (index < 0 || index >= attributeData.ConstructorArguments.Length) @@ -1076,6 +1334,10 @@ sealed class EnumGroupBuilder(string enumName, string enumNamespace, bool enumGe public bool EnumGeneratesFullNameConstant { get; } = enumGeneratesFullNameConstant; + public string? BulkMarkerFieldName { get; set; } + + public List MarkerFieldNames { get; } = []; + public List Values { get; } = []; public TypeLibraryEnumGroupModel ToModel() => @@ -1083,7 +1345,8 @@ public TypeLibraryEnumGroupModel ToModel() => EnumName, EnumNamespace, EnumGeneratesFullNameConstant, - new EquatableArray([.. Values]) + new EquatableArray([.. Values]), + new EquatableArray([.. MarkerFieldNames]) ); } } diff --git a/src/src/SourceGeneratorFramework.Generators/Model/TypeLibraryModel.cs b/src/src/SourceGeneratorFramework.Generators/Model/TypeLibraryModel.cs index 07edf30..5a217f4 100644 --- a/src/src/SourceGeneratorFramework.Generators/Model/TypeLibraryModel.cs +++ b/src/src/SourceGeneratorFramework.Generators/Model/TypeLibraryModel.cs @@ -34,7 +34,8 @@ sealed record TypeLibraryEnumGroupModel( string EnumName, string EnumNamespace, bool EnumGeneratesFullNameConstant, - EquatableArray Values + EquatableArray Values, + EquatableArray MarkerFieldNames = default ); /// diff --git a/src/src/SourceGeneratorFramework.Testing/EmittedAssembly.cs b/src/src/SourceGeneratorFramework.Testing/EmittedAssembly.cs index a01fd68..75e44b7 100644 --- a/src/src/SourceGeneratorFramework.Testing/EmittedAssembly.cs +++ b/src/src/SourceGeneratorFramework.Testing/EmittedAssembly.cs @@ -46,13 +46,15 @@ public Assembly? Assembly if (field is null) { - #if NET8_0_OR_GREATER + #pragma warning disable format +#if NET8_0_OR_GREATER field = _loadContext!.LoadFromStream( new MemoryStream(_bytes, writable: false) ); #else field = Assembly.Load(_bytes); +#pragma warning restore format #endif } diff --git a/src/src/SourceGeneratorFramework.Testing/SourceGeneratorTestOptions.cs b/src/src/SourceGeneratorFramework.Testing/SourceGeneratorTestOptions.cs index 9d42fb4..7c8ea1c 100644 --- a/src/src/SourceGeneratorFramework.Testing/SourceGeneratorTestOptions.cs +++ b/src/src/SourceGeneratorFramework.Testing/SourceGeneratorTestOptions.cs @@ -74,7 +74,7 @@ public SourceGeneratorTestOptions() // DisableSourceGeneratorPropertyName = source.DisableSourceGeneratorPropertyName; // DisableSourceGeneratorValue = source.DisableSourceGeneratorValue; - // AnalyzerConfigOptions = [with(source.AnalyzerConfigOptions)]; + // AnalyzerConfigOptions = new(source.AnalyzerConfigOptions)]; // TestOutput = source.TestOutput; // CompilationAssemblyName = source.CompilationAssemblyName; diff --git a/src/src/SourceGeneratorShared/OperatorNames.cs b/src/src/SourceGeneratorShared/OperatorNames.cs new file mode 100644 index 0000000..6695488 --- /dev/null +++ b/src/src/SourceGeneratorShared/OperatorNames.cs @@ -0,0 +1,401 @@ +namespace Purview.SourceGeneratorFramework; + +/// +/// Contains the names of the operators as defined in the C# language specification. These names are used for operator +/// overloading and are recognized by the C# compiler. +/// +[System.Diagnostics.CodeAnalysis.SuppressMessage( + "Design", + "CA1034:Nested types should not be visible", + Justification = "" +)] +public static class OperatorNames +{ + /// + /// Contains the names of the unary operators as defined in the C# language specification. These names are used for + /// operator overloading and are recognized by the C# compiler. + /// + public static class Unary + { + /// + /// The name of the unary plus operator, which is used to indicate a positive value. In C#, this operator does not + /// change the value of its operand, but it can be overloaded in user-defined types. + /// + public const string UnaryPlusOperatorName = "op_UnaryPlus"; + + /// + /// The name of the unary negation operator, which is used to negate a value. In C#, this operator changes the sign of + /// its operand and can be overloaded in user-defined types. + /// + public const string UnaryNegationOperatorName = "op_UnaryNegation"; + + /// + /// The name of the checked unary negation operator, which is used to negate a value with overflow checking. In C#, + /// this operator can be overloaded in user-defined types. + /// + public const string CheckedUnaryNegationOperatorName = "op_CheckedUnaryNegation"; + + /// + /// The name of the logical NOT operator, which is used to invert a boolean value. In C#, this operator changes true to + /// false and false to true, and it can be overloaded in user-defined types. + /// + public const string LogicalNotOperatorName = "op_LogicalNot"; + + /// + /// The name of the bitwise NOT operator, which is used to invert the bits of an integer value. In C#, this operator + /// can be overloaded in user-defined types. + /// + public const string OnesComplementOperatorName = "op_OnesComplement"; + + /// + /// The name of the increment operator, which is used to increase a value by one. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string IncrementOperatorName = "op_Increment"; + + /// + /// The name of the checked increment operator, which is used to increase a value by one with overflow checking. In C#, + /// this operator can be overloaded in user-defined types. + /// + public const string CheckedIncrementOperatorName = "op_CheckedIncrement"; + + /// + /// The name of the decrement operator, which is used to decrease a value by one. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string DecrementOperatorName = "op_Decrement"; + + /// + /// The name of the checked decrement operator, which is used to decrease a value by one with overflow checking. In C#, + /// this operator can be overloaded in user-defined types. + /// + public const string CheckedDecrementOperatorName = "op_CheckedDecrement"; + + /// + /// The name of the true operator, which is used to determine if a value is true. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string TrueOperatorName = "op_True"; + + /// + /// The name of the false operator, which is used to determine if a value is false. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string FalseOperatorName = "op_False"; + } + + /// + /// Contains the names of the arithmetic operators as defined in the C# language specification. These names are used + /// for operator overloading and are recognized by the C# compiler. + /// + public static class Arithmetic + { + /// + /// The name of the addition operator, which is used to add two values. In C#, this operator can be overloaded in + /// user-defined types. + /// + public const string AdditionOperatorName = "op_Addition"; + + /// + /// The name of the checked addition operator, which is used to add two values with overflow checking. In C#, this + /// operator can be overloaded in user-defined types. + /// + public const string CheckedAdditionOperatorName = "op_CheckedAddition"; + + /// + /// The name of the subtraction operator, which is used to subtract one value from another. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string SubtractionOperatorName = "op_Subtraction"; + + /// + /// The name of the checked subtraction operator, which is used to subtract one value from another with overflow + /// checking. In C#, this operator can be overloaded in user-defined types. + /// + public const string CheckedSubtractionOperatorName = "op_CheckedSubtraction"; + + /// + /// The name of the multiplication operator, which is used to multiply two values. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string MultiplyOperatorName = "op_Multiply"; + + /// + /// The name of the checked multiplication operator, which is used to multiply two values with overflow checking. In + /// C#, this operator can be overloaded in user-defined types. + /// + public const string CheckedMultiplyOperatorName = "op_CheckedMultiply"; + + /// + /// The name of the division operator, which is used to divide one value by another. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string DivisionOperatorName = "op_Division"; + + /// + /// The name of the checked division operator, which is used to divide one value by another with overflow checking. In + /// C#, this operator can be overloaded in user-defined types. + /// + public const string CheckedDivisionOperatorName = "op_CheckedDivision"; + + /// + /// The name of the modulus operator, which is used to find the remainder of a division operation. In C#, this operator + /// can be overloaded in user-defined types. + /// + public const string ModulusOperatorName = "op_Modulus"; + } + + /// + /// Contains the names of the bitwise operators as defined in the C# language specification. These names are used for + /// operator overloading and are recognized by the C# compiler. + /// + public static class Bitwise + { + /// + /// The name of the bitwise AND operator, which is used to perform a logical AND operation on each pair of bits in two + /// values. In C#, this operator can be overloaded in user-defined types. + /// + public const string BitwiseAndOperatorName = "op_BitwiseAnd"; + + /// + /// The name of the bitwise OR operator, which is used to perform a logical OR operation on each pair of bits in two + /// values. In C#, this operator can be overloaded in user-defined types. + /// + public const string BitwiseOrOperatorName = "op_BitwiseOr"; + + /// + /// The name of the exclusive OR operator, which is used to perform a logical XOR operation on each pair of bits in two + /// values. In C#, this operator can be overloaded in user-defined types. + /// + public const string ExclusiveOrOperatorName = "op_ExclusiveOr"; + } + + /// + /// Contains the names of the shift operators as defined in the C# language specification. These names are used for + /// operator overloading and are recognized by the C# compiler. + /// + public static class Shift + { + /// + /// The name of the left shift operator, which is used to shift the bits of a value to the left by a specified number + /// of positions. In C#, this operator can be overloaded in user-defined types. + /// + public const string LeftShiftOperatorName = "op_LeftShift"; + + /// + /// The name of the right shift operator, which is used to shift the bits of a value to the right by a specified number + /// of positions. In C#, this operator can be overloaded in user-defined types. + /// + public const string RightShiftOperatorName = "op_RightShift"; + + /// + /// The name of the unsigned right shift operator, which is used to shift the bits of a value to the right by a + /// specified number of positions, filling the leftmost bits with zeros. In C#, this operator can be overloaded in + /// user-defined types. + /// + public const string UnsignedRightShiftOperatorName = "op_UnsignedRightShift"; + } + + /// + /// Contains the names of the equality and relational operators as defined in the C# language specification. These + /// names are used for operator overloading and are recognized by the C# compiler. + /// + public static class EqualityAndRelational + { + /// + /// The name of the equality operator, which is used to compare two values for equality. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string EqualityOperatorName = "op_Equality"; + + /// + /// The name of the inequality operator, which is used to compare two values for inequality. In C#, this operator can + /// be overloaded in user-defined types. + /// + public const string InequalityOperatorName = "op_Inequality"; + + /// + /// The name of the less than operator, which is used to compare two values to determine if the first is less than the + /// second. In C#, this operator can be overloaded in user-defined types. + /// + public const string LessThanOperatorName = "op_LessThan"; + + /// + /// The name of the greater than operator, which is used to compare two values to determine if the first is greater + /// than the second. In C#, this operator can be overloaded in user-defined types. + /// + public const string GreaterThanOperatorName = "op_GreaterThan"; + + /// + /// The name of the less than or equal to operator, which is used to compare two values to determine if the first is + /// less than or equal to the second. In C#, this operator can be overloaded in user-defined types. + /// + public const string LessThanOrEqualOperatorName = "op_LessThanOrEqual"; + + /// + /// The name of the greater than or equal to operator, which is used to compare two values to determine if the first is + /// greater than or equal to the second. In C#, this operator can be overloaded in user-defined types. + /// + public const string GreaterThanOrEqualOperatorName = "op_GreaterThanOrEqual"; + } + + /// + /// Contains the names of the conversion operators as defined in the C# language specification. These names are used + /// for operator overloading and are recognized by the C# compiler. + /// + public static class Conversion + { + /// + /// The name of the implicit conversion operator, which is used to define a conversion from one type to another that + /// can be performed implicitly. In C#, this operator can be overloaded in user-defined types. + /// + public const string ImplicitOperatorName = "op_Implicit"; + + /// + /// The name of the explicit conversion operator, which is used to define a conversion from one type to another that + /// can be performed explicitly. In C#, this operator can be overloaded in user-defined types. + /// + public const string ExplicitOperatorName = "op_Explicit"; + + /// + /// The name of the checked explicit conversion operator, which is used to define a conversion from one type to another + /// that can be performed explicitly and will throw an exception if the conversion results in an overflow. In C#, this operator can be overloaded in user-defined types. + /// + public const string CheckedExplicitOperatorName = "op_CheckedExplicit"; + } + + /// + /// Contains the names of the compound assignment and reserved operators as defined in the C# language specification. + /// These names are used for operator overloading and are recognized by the C# compiler. + /// + public static class CompoundAndReserved + { + /// + /// The name of the addition assignment operator, which is used to add a value to a variable and assign the result back + /// to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string AdditionAssignmentOperatorName = "op_AdditionAssignment"; + + /// + /// The name of the subtraction assignment operator, which is used to subtract a value from a variable and assign the + /// result back to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string SubtractionAssignmentOperatorName = "op_SubtractionAssignment"; + + /// + /// The name of the multiplication assignment operator, which is used to multiply a variable by a value and assign the + /// result back to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string MultiplicationAssignmentOperatorName = "op_MultiplicationAssignment"; + + /// + /// The name of the division assignment operator, which is used to divide a variable by a value and assign the result + /// back to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string DivisionAssignmentOperatorName = "op_DivisionAssignment"; + + /// + /// The name of the modulus assignment operator, which is used to divide a variable by a value and assign the remainder + /// back to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string ModulusAssignmentOperatorName = "op_ModulusAssignment"; + + /// + /// The name of the bitwise AND assignment operator, which is used to perform a bitwise AND operation on a variable + /// and assign the result back to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string BitwiseAndAssignmentOperatorName = "op_BitwiseAndAssignment"; + + /// + /// The name of the bitwise OR assignment operator, which is used to perform a bitwise OR operation on a variable and + /// assign the result back to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string BitwiseOrAssignmentOperatorName = "op_BitwiseOrAssignment"; + + /// + /// The name of the exclusive OR assignment operator, which is used to perform a bitwise XOR operation on a variable + /// and assign the result back to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string ExclusiveOrAssignmentOperatorName = "op_ExclusiveOrAssignment"; + + /// + /// The name of the left shift assignment operator, which is used to perform a left shift operation on a variable and + /// assign the result back to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string LeftShiftAssignmentOperatorName = "op_LeftShiftAssignment"; + + /// + /// The name of the right shift assignment operator, which is used to perform a right shift operation on a variable and + /// assign the result back to the variable. In C#, this operator can be overloaded in user-defined types. + /// + public const string RightShiftAssignmentOperatorName = "op_RightShiftAssignment"; + + /// + /// The name of the unsigned right shift assignment operator, which is used to perform an unsigned right shift + /// operation on a variable and assign the result back to the variable. In C#, this operator can be overloaded in + /// user-defined types. + /// + public const string UnsignedRightShiftAssignmentOperatorName = "op_UnsignedRightShiftAssignment"; + } + + /// + /// Contains the names of the reserved operators as defined in the C# language specification. These names are used for + /// operator overloading and are recognized by the C# compiler. + /// + public static class Reserved + { + /// + /// The name of the address-of operator, which is used to obtain the memory address of a variable. In C#, this operator + /// can be overloaded in user-defined types. + /// + public const string AddressOfOperatorName = "op_AddressOf"; + + /// + /// The name of the assign operator, which is used to assign a value to a variable. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string AssignOperatorName = "op_Assign"; + + /// + /// The name of the comma operator, which is used to separate expressions in a statement. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string CommaOperatorName = "op_Comma"; + + /// + /// The name of the conditional operator, which is used to evaluate a condition and return one of two values based on + /// the result of the condition. In C#, this operator can be overloaded in user-defined types. + /// + public const string LogicalAndOperatorName = "op_LogicalAnd"; + + /// + /// The name of the logical OR operator, which is used to evaluate two boolean expressions and return true if either + /// of the expressions is true. In C#, this operator can be overloaded in user-defined types. + /// + public const string LogicalOrOperatorName = "op_LogicalOr"; + + /// + /// The name of the member selection operator, which is used to access a member of a type. In C#, this operator can be + /// overloaded in user-defined types. + /// + public const string MemberSelectionOperatorName = "op_MemberSelection"; + + /// + /// The name of the pointer dereference operator, which is used to access the value pointed to by a pointer. In C#, + /// this operator can be overloaded in user-defined types. + /// + public const string PointerDereferenceOperatorName = "op_PointerDereference"; + + /// + /// The name of the pointer to member selection operator, which is used to access a member of a type through a pointer. + /// In C#, this operator can be overloaded in user-defined types. + /// + public const string PointerToMemberSelectionOperatorName = "op_PointerToMemberSelection"; + + /// + /// The name of the signed right shift operator, which is used to shift the bits of a value to the right by a specified + /// number of positions. In C#, this operator can be overloaded in user-defined types. + /// + public const string SignedRightShiftOperatorName = "op_SignedRightShift"; + } +} diff --git a/src/src/SourceGeneratorShared/PurviewTypeLibrary.cs b/src/src/SourceGeneratorShared/PurviewTypeLibrary.cs index 69f3b93..ba0642b 100644 --- a/src/src/SourceGeneratorShared/PurviewTypeLibrary.cs +++ b/src/src/SourceGeneratorShared/PurviewTypeLibrary.cs @@ -1,6 +1,4 @@ -using System.Collections.Concurrent; using System.Collections.Immutable; -using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; namespace Purview.SourceGeneratorFramework; @@ -14,7 +12,7 @@ namespace Purview.SourceGeneratorFramework; public static partial class PurviewTypeLibrary { /// - /// Common types from the System namespace. + /// Common types from the System namespace hierarchy. /// public static class System { @@ -24,97 +22,186 @@ public static class System public const string Namespace = "System"; /// - /// . + /// The System.Attribute type. /// public static readonly TypeIdentity Attribute = TypeIdentity.Create(); /// - /// . + /// The System.AttributeUsageAttribute type. + /// + public static readonly TypeIdentity AttributeUsageAttribute = TypeIdentity.Create(); + + /// + /// The System.FlagsAttribute type. + /// + public static readonly TypeIdentity FlagsAttribute = TypeIdentity.Create(); + + /// + /// The System.ObsoleteAttribute type. + /// + public static readonly TypeIdentity ObsoleteAttribute = TypeIdentity.Create(); + + /// + /// The System.SerializableAttribute type. + /// + public static readonly TypeIdentity SerializableAttribute = TypeIdentity.Create(); + + /// + /// The System.IComparable type. The generic + /// System.IComparable{T} form has arity 1. + /// + public static readonly TypeIdentity IComparable = TypeIdentity.Create(); + + /// + /// The generic System.IEquatable{T} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity IEquatable = new(nameof(IEquatable), Namespace, 1); + + /// + /// The generic System.Nullable{T} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity Nullable = new(nameof(Nullable), Namespace, 1); + + /// + /// The System.Array type. /// public static readonly TypeIdentity Array = TypeIdentity.Create(); /// - /// . + /// The System.Type type. /// public static readonly TypeIdentity Type = TypeIdentity.Create(); /// - /// . + /// The System.Enum type. + /// + public static readonly TypeIdentity Enum = TypeIdentity.Create(); + + /// + /// The System.ValueType type. + /// + public static readonly TypeIdentity ValueType = TypeIdentity.Create(); + + /// + /// The System.Delegate type. + /// + public static readonly TypeIdentity Delegate = TypeIdentity.Create(); + + /// + /// The System.MulticastDelegate type. + /// + public static readonly TypeIdentity MulticastDelegate = TypeIdentity.Create(); + + /// + /// The System.Uri type. + /// + public static readonly TypeIdentity Uri = TypeIdentity.Create(); + + /// + /// The System.Version type. + /// + public static readonly TypeIdentity Version = TypeIdentity.Create(); + + /// + /// The System.IFormattable type. + /// + public static readonly TypeIdentity IFormattable = TypeIdentity.Create(); + + /// + /// The System.IConvertible type. + /// + public static readonly TypeIdentity IConvertible = TypeIdentity.Create(); + + /// + /// The generic System.Lazy{T} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity Lazy = new(nameof(Lazy), Namespace, 1); + + /// + /// The System.Guid type. + /// + public static readonly TypeIdentity Guid = TypeIdentity.Create(); + + /// + /// The type. /// public static readonly TypeIdentity Boolean = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Byte = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity SByte = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Char = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Decimal = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Double = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Float = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Int32 = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity UInt32 = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Int64 = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity UInt64 = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Int16 = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity UInt16 = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity String = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Object = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity Void = new("void", null); @@ -122,98 +209,128 @@ public static class System /// The C# null literal, presented as an identity for use in value positions. /// /// - /// Renders as null and converts to the "null" expression where a string value is - /// expected, such as an initializer, default value, argument or return value. It is not a type and - /// is rejected in type positions. . + /// Renders as null and converts to the "null" expression where a + /// string value is expected, such as an initializer, default value, argument or + /// return value. It is not a type and is rejected in type positions. /// public static readonly TypeIdentity Null = TypeIdentity.Null; /// - /// . + /// The type. /// public static readonly TypeIdentity IntPtr = TypeIdentity.Create(); /// - /// . + /// The type. /// public static readonly TypeIdentity UIntPtr = TypeIdentity.Create(); /// - /// . + /// The System.Action delegate family. Generic forms have arities from 1 through 16. /// - public static readonly TypeIdentity Action = TypeIdentity.Create(); + /// + /// If you require a normal action with no arity, call this with the method + /// and a value of 0. + /// + public static readonly TypeIdentity Action = new(nameof(Action), Namespace, 1); /// - /// The Func<TResult> open generic definition, the lowest arity in the - /// Func family. Use for higher arities, up to - /// Func<T1…T16, TResult> (arity 17). + /// The generic System.Func{TResult} delegate family. + /// Generic forms have arities from 1 through 17. /// public static readonly TypeIdentity Func = new(nameof(Func), Namespace, 1); /// - /// . + /// The generic System.Predicate{T} delegate type. + /// The type has arity 1. + /// + public static readonly TypeIdentity Predicate = new(nameof(Predicate), Namespace, 1); + + /// + /// The generic System.Comparison{T} delegate type. + /// The type has arity 1. + /// + public static readonly TypeIdentity Comparison = new(nameof(Comparison), Namespace, 1); + + /// + /// The System.Exception type. /// public static readonly TypeIdentity Exception = TypeIdentity.Create(); /// - /// . + /// The System.ArgumentException type. + /// + public static readonly TypeIdentity ArgumentException = TypeIdentity.Create(); + + /// + /// The System.ArgumentNullException type. /// public static readonly TypeIdentity ArgumentNullException = TypeIdentity.Create(); /// - /// . + /// The System.ArgumentOutOfRangeException type. /// public static readonly TypeIdentity ArgumentOutOfRangeException = TypeIdentity.Create(); /// - /// . + /// The System.InvalidOperationException type. /// - public static readonly TypeIdentity ArgumentException = TypeIdentity.Create(); + public static readonly TypeIdentity InvalidOperationException = + TypeIdentity.Create(); /// - /// . + /// The System.NotSupportedException type. /// - public static readonly TypeIdentity InvalidOperationException = - TypeIdentity.Create(); + public static readonly TypeIdentity NotSupportedException = TypeIdentity.Create(); + + /// + /// The System.NotImplementedException type. + /// + public static readonly TypeIdentity NotImplementedException = TypeIdentity.Create(); + + /// + /// The System.FormatException type. + /// + public static readonly TypeIdentity FormatException = TypeIdentity.Create(); /// - /// . + /// The System.IDisposable type. /// public static readonly TypeIdentity IDisposable = TypeIdentity.Create(); /// - /// System.IAsyncDisposable. + /// The System.IAsyncDisposable type. /// - public static readonly TypeIdentity IAsyncDisposable = new(nameof(IAsyncDisposable), "System"); + public static readonly TypeIdentity IAsyncDisposable = new(nameof(IAsyncDisposable), Namespace); /// - /// . + /// The System.DateTimeOffset type. /// public static readonly TypeIdentity DateTimeOffset = TypeIdentity.Create(); /// - /// . + /// The System.DateTime type. /// public static readonly TypeIdentity DateTime = TypeIdentity.Create(); /// - /// . + /// The System.TimeSpan type. /// public static readonly TypeIdentity TimeSpan = TypeIdentity.Create(); /// - /// DateOnly + /// The System.DateOnly type. /// - public static readonly TypeIdentity DateOnly = new(nameof(DateOnly), "System"); + public static readonly TypeIdentity DateOnly = new(nameof(DateOnly), Namespace); /// - /// TimeOnly + /// The System.TimeOnly type. /// - public static readonly TypeIdentity TimeOnly = new(nameof(TimeOnly), "System"); + public static readonly TypeIdentity TimeOnly = new(nameof(TimeOnly), Namespace); /// - /// The System.Threading namespace. + /// Common types from the System.Threading namespace hierarchy. /// public static class Threading { @@ -223,8 +340,42 @@ public static class Threading public const string Namespace = "System.Threading"; /// - /// . + /// The System.Threading.CancellationToken type. + /// + public static readonly TypeIdentity CancellationToken = TypeIdentity.Create(); + + /// + /// The System.Threading.CancellationTokenSource type. + /// + public static readonly TypeIdentity CancellationTokenSource = + TypeIdentity.Create(); + + /// + /// The System.Threading.SemaphoreSlim type. /// + public static readonly TypeIdentity SemaphoreSlim = TypeIdentity.Create(); + + /// + /// The System.Threading.SynchronizationContext type. + /// + public static readonly TypeIdentity SynchronizationContext = TypeIdentity.Create(); + + /// + /// The System.Threading.Interlocked type. + /// + public static readonly TypeIdentity Interlocked = new(nameof(Interlocked), Namespace); + + /// + /// The System.Threading.Monitor type. + /// + public static readonly TypeIdentity Monitor = new(nameof(Monitor), Namespace); + + /// + /// The generic System.Threading.AsyncLocal{T} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity AsyncLocal = new(nameof(AsyncLocal), Namespace, 1); + public static class Tasks { /// @@ -233,24 +384,60 @@ public static class Tasks public const string Namespace = "System.Threading.Tasks"; /// - /// . + /// The System.Threading.Tasks.Task type. + /// The generic Task{TResult} form has arity 1. + /// + public static readonly TypeIdentity Task = TypeIdentity.Create(); + + /// + /// The System.Threading.Tasks.ValueTask type. + /// The generic ValueTask{TResult} form has arity 1. /// - public static readonly TypeIdentity CancellationToken = new(typeof(CancellationToken)); + public static readonly TypeIdentity ValueTask = TypeIdentity.Create(); /// - /// . + /// The System.Threading.Tasks.TaskFactory type. + /// The generic TaskFactory{TResult} form has arity 1. /// - public static readonly TypeIdentity ValueTask = new(typeof(ValueTask)); + public static readonly TypeIdentity TaskFactory = new(nameof(TaskFactory), Namespace, 1); /// - /// . + /// The System.Threading.Tasks.TaskScheduler type. /// - public static readonly TypeIdentity Task = new(typeof(Task)); + public static readonly TypeIdentity TaskScheduler = new(nameof(TaskScheduler), Namespace); + + /// + /// The System.Threading.Tasks.TaskStatus type. + /// + public static readonly TypeIdentity TaskStatus = new(nameof(TaskStatus), Namespace); + + /// + /// The System.Threading.Tasks.TaskCreationOptions type. + /// + public static readonly TypeIdentity TaskCreationOptions = new(nameof(TaskCreationOptions), Namespace); + + /// + /// The System.Threading.Tasks.TaskContinuationOptions type. + /// + public static readonly TypeIdentity TaskContinuationOptions = new( + nameof(TaskContinuationOptions), + Namespace + ); + + /// + /// The generic System.Threading.Tasks.TaskCompletionSource{TResult} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity TaskCompletionSource = new( + nameof(TaskCompletionSource), + Namespace, + 1 + ); } } /// - /// The System.Globalization namespace. + /// Common types from the System.Globalization namespace. /// public static partial class Globalization { @@ -260,37 +447,49 @@ public static partial class Globalization public const string Namespace = "System.Globalization"; /// - /// . + /// The System.Globalization.CultureInfo type. /// public static readonly TypeIdentity CultureInfo = TypeIdentity.Create(); /// - /// . + /// The System.Globalization.CultureNotFoundException type. + /// + public static readonly TypeIdentity CultureNotFoundException = + TypeIdentity.Create(); + + /// + /// The System.Globalization.NumberStyles type. /// public static readonly TypeIdentity NumberStyles = TypeIdentity.Create(); /// - /// . + /// The System.Globalization.DateTimeStyles type. + /// + public static readonly TypeIdentity DateTimeStyles = + TypeIdentity.Create(); + + /// + /// The System.Globalization.TextInfo type. /// public static readonly TypeIdentity TextInfo = TypeIdentity.Create(); /// - /// . + /// The System.Globalization.DateTimeFormatInfo type. /// public static readonly TypeIdentity DateTimeFormatInfo = TypeIdentity.Create(); /// - /// . + /// The System.Globalization.NumberFormatInfo type. /// public static readonly TypeIdentity NumberFormatInfo = TypeIdentity.Create(); } /// - /// The System.Collections namespace. + /// Common types from the System.Collections namespace hierarchy. /// public static partial class Collections { @@ -300,57 +499,63 @@ public static partial class Collections public const string Namespace = "System.Collections"; /// - /// . + /// The System.Collections.IEnumerable type. /// public static readonly TypeIdentity IEnumerable = TypeIdentity.Create(); /// - /// . + /// The System.Collections.IEnumerator type. + /// + public static readonly TypeIdentity IEnumerator = + TypeIdentity.Create(); + + /// + /// The System.Collections.IDictionary type. /// public static readonly TypeIdentity IDictionary = TypeIdentity.Create(); /// - /// . + /// The System.Collections.ICollection type. /// public static readonly TypeIdentity ICollection = TypeIdentity.Create(); /// - /// . + /// The System.Collections.IList type. /// public static readonly TypeIdentity IList = TypeIdentity.Create(); /// - /// . + /// The System.Collections.ArrayList type. /// public static readonly TypeIdentity ArrayList = TypeIdentity.Create(); /// - /// . + /// The System.Collections.SortedList type. /// public static readonly TypeIdentity SortedList = TypeIdentity.Create(); /// - /// . + /// The System.Collections.Queue type. /// public static readonly TypeIdentity Queue = TypeIdentity.Create(); /// - /// . + /// The System.Collections.Stack type. /// - public static readonly TypeIdentity ReadOnlyCollectionBase = - TypeIdentity.Create(); + public static readonly TypeIdentity Stack = TypeIdentity.Create(); /// - /// . + /// The System.Collections.ReadOnlyCollectionBase type. /// - public static readonly TypeIdentity Stack = TypeIdentity.Create(); + public static readonly TypeIdentity ReadOnlyCollectionBase = + TypeIdentity.Create(); /// - /// The System.Collections.Generic namespace. + /// Common types from the System.Collections.Generic namespace. /// public static partial class Generic { @@ -360,109 +565,171 @@ public static partial class Generic public const string Namespace = "System.Collections.Generic"; /// - /// . + /// The generic System.Collections.Generic.IEnumerable{T} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity IEnumerable = new(nameof(IEnumerable), Namespace, 1); + + /// + /// The generic System.Collections.Generic.IEnumerator{T} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity IEnumerator = new(nameof(IEnumerator), Namespace, 1); + + /// + /// The generic System.Collections.Generic.ICollection{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity IEnumerable = new(typeof(IEnumerable<>)); + public static readonly TypeIdentity ICollection = new(nameof(ICollection), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.IList{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity IList = new(typeof(IList<>)); + public static readonly TypeIdentity IList = new(nameof(IList), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.IReadOnlyCollection{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ICollection = new(typeof(ICollection<>)); + public static readonly TypeIdentity IReadOnlyCollection = new( + nameof(IReadOnlyCollection), + Namespace, + 1 + ); /// - /// . + /// The generic System.Collections.Generic.IReadOnlyList{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity IReadOnlyCollection = new(typeof(IReadOnlyCollection<>)); + public static readonly TypeIdentity IReadOnlyList = new(nameof(IReadOnlyList), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.ISet{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity IReadOnlyList = new(typeof(IReadOnlyList<>)); + public static readonly TypeIdentity ISet = new(nameof(ISet), Namespace, 1); /// - /// The IReadOnlySet{T} interface. Declared by name because the type is not available to the - /// netstandard2.0 build of this assembly. + /// The generic System.Collections.Generic.IReadOnlySet{T} type. + /// The type has arity 1. /// public static readonly TypeIdentity IReadOnlySet = new(nameof(IReadOnlySet), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.IDictionary{TKey, TValue} type. + /// The type has arity 2. + /// + public static readonly TypeIdentity IDictionary = new(nameof(IDictionary), Namespace, 2); + + /// + /// The generic System.Collections.Generic.IReadOnlyDictionary{TKey, TValue} type. + /// The type has arity 2. + /// + public static readonly TypeIdentity IReadOnlyDictionary = new( + nameof(IReadOnlyDictionary), + Namespace, + 2 + ); + + /// + /// The generic System.Collections.Generic.List{T} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity List = new(nameof(List), Namespace, 1); + + /// + /// The generic System.Collections.Generic.Dictionary{TKey, TValue} type. + /// The type has arity 2. + /// + public static readonly TypeIdentity Dictionary = new(nameof(Dictionary), Namespace, 2); + + /// + /// The generic System.Collections.Generic.HashSet{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity IEnumerator = new(typeof(IEnumerator<>)); + public static readonly TypeIdentity HashSet = new(nameof(HashSet), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.SortedDictionary{TKey, TValue} type. + /// The type has arity 2. /// - public static readonly TypeIdentity List = new(typeof(List<>)); + public static readonly TypeIdentity SortedDictionary = new(nameof(SortedDictionary), Namespace, 2); /// - /// . + /// The generic System.Collections.Generic.SortedSet{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity IDictionary = new(typeof(IDictionary<,>)); + public static readonly TypeIdentity SortedSet = new(nameof(SortedSet), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.SortedList{TKey, TValue} type. + /// The type has arity 2. /// - public static readonly TypeIdentity IReadOnlyDictionary = new(typeof(IReadOnlyDictionary<,>)); + public static readonly TypeIdentity SortedList = new(nameof(SortedList), Namespace, 2); /// - /// . + /// The generic System.Collections.Generic.LinkedList{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity Dictionary = new(typeof(Dictionary<,>)); + public static readonly TypeIdentity LinkedList = new(nameof(LinkedList), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.LinkedListNode{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ISet = new(typeof(ISet<>)); + public static readonly TypeIdentity LinkedListNode = new(nameof(LinkedListNode), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.Queue{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity HashSet = new(typeof(HashSet<>)); + public static readonly TypeIdentity Queue = new(nameof(Queue), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.Stack{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity SortedDictionary = new(typeof(SortedDictionary<,>)); + public static readonly TypeIdentity Stack = new(nameof(Stack), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.KeyValuePair{TKey, TValue} type. + /// The type has arity 2. /// - public static readonly TypeIdentity SortedSet = new(typeof(SortedSet<>)); + public static readonly TypeIdentity KeyValuePair = new(nameof(KeyValuePair), Namespace, 2); /// - /// . + /// The System.Collections.Generic.KeyNotFoundException type. /// - public static readonly TypeIdentity SortedList = new(typeof(SortedList<,>)); + public static readonly TypeIdentity KeyNotFoundException = new(nameof(KeyNotFoundException), Namespace); /// - /// . + /// The generic System.Collections.Generic.IComparer{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity LinkedList = new(typeof(LinkedList<>)); + public static readonly TypeIdentity IComparer = new(nameof(IComparer), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.IEqualityComparer{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity Queue = new(typeof(Queue<>)); + public static readonly TypeIdentity IEqualityComparer = new(nameof(IEqualityComparer), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.Comparer{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity Stack = new(typeof(Stack<>)); + public static readonly TypeIdentity Comparer = new(nameof(Comparer), Namespace, 1); /// - /// . + /// The generic System.Collections.Generic.EqualityComparer{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity KeyValuePair = new(typeof(KeyValuePair<,>)); + public static readonly TypeIdentity EqualityComparer = new(nameof(EqualityComparer), Namespace, 1); } /// - /// The System.Collections.Concurrent namespace. + /// Common types from the System.Collections.Concurrent namespace. /// public static partial class Concurrent { @@ -472,184 +739,210 @@ public static partial class Concurrent public const string Namespace = "System.Collections.Concurrent"; /// - /// . + /// The generic + /// System.Collections.Concurrent.ConcurrentDictionary{TKey, TValue} type. + /// The type has arity 2. /// - public static readonly TypeIdentity ConcurrentDictionary = new(typeof(ConcurrentDictionary<,>)); + public static readonly TypeIdentity ConcurrentDictionary = new( + nameof(ConcurrentDictionary), + Namespace, + 2 + ); /// - /// . + /// The generic System.Collections.Concurrent.ConcurrentQueue{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ConcurrentQueue = new(typeof(ConcurrentQueue<>)); + public static readonly TypeIdentity ConcurrentQueue = new(nameof(ConcurrentQueue), Namespace, 1); /// - /// . + /// The generic System.Collections.Concurrent.ConcurrentStack{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ConcurrentStack = new(typeof(ConcurrentStack<>)); + public static readonly TypeIdentity ConcurrentStack = new(nameof(ConcurrentStack), Namespace, 1); /// - /// . + /// The generic System.Collections.Concurrent.ConcurrentBag{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ConcurrentBag = new(typeof(ConcurrentBag<>)); + public static readonly TypeIdentity ConcurrentBag = new(nameof(ConcurrentBag), Namespace, 1); /// - /// . + /// The generic System.Collections.Concurrent.BlockingCollection{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity BlockingCollection = new(typeof(BlockingCollection<>)); + public static readonly TypeIdentity BlockingCollection = new(nameof(BlockingCollection), Namespace, 1); /// - /// . + /// The generic + /// System.Collections.Concurrent.IProducerConsumerCollection{T} type. + /// The type has arity 1. /// public static readonly TypeIdentity IProducerConsumerCollection = new( - typeof(IProducerConsumerCollection<>) + nameof(IProducerConsumerCollection), + Namespace, + 1 ); } /// - /// The System.Collections.ObjectModel namespace. + /// Common types from the System.Collections.ObjectModel namespace. /// public static partial class ObjectModel { + /// + /// The System.Collections.ObjectModel namespace. + /// public const string Namespace = "System.Collections.ObjectModel"; /// - /// . + /// The generic System.Collections.ObjectModel.Collection{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity Collection = new(typeof(Collection<>)); + public static readonly TypeIdentity Collection = new(nameof(Collection), Namespace, 1); /// - /// . + /// The generic System.Collections.ObjectModel.ReadOnlyCollection{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ReadOnlyCollection = new(typeof(ReadOnlyCollection<>)); + public static readonly TypeIdentity ReadOnlyCollection = new(nameof(ReadOnlyCollection), Namespace, 1); /// - /// . + /// The generic + /// System.Collections.ObjectModel.ReadOnlyDictionary{TKey, TValue} type. + /// The type has arity 2. /// - public static readonly TypeIdentity ReadOnlyDictionary = new(typeof(ReadOnlyDictionary<,>)); + public static readonly TypeIdentity ReadOnlyDictionary = new(nameof(ReadOnlyDictionary), Namespace, 2); /// - /// . + /// The generic System.Collections.ObjectModel.KeyedCollection{TKey, TItem} type. + /// The type has arity 2. /// - public static readonly TypeIdentity KeyedCollection = new(typeof(KeyedCollection<,>)); + public static readonly TypeIdentity KeyedCollection = new(nameof(KeyedCollection), Namespace, 2); /// - /// . + /// The generic + /// System.Collections.ObjectModel.ObservableCollection{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ObservableCollection = new(typeof(ObservableCollection<>)); + public static readonly TypeIdentity ObservableCollection = new( + nameof(ObservableCollection), + Namespace, + 1 + ); } /// - /// The System.Collections.Immutable namespace. + /// Common types from the System.Collections.Immutable namespace. /// public static partial class Immutable { - public const string Namespace = "System.Collections.Immutable"; - /// - /// . + /// The System.Collections.Immutable namespace. /// - public static readonly TypeIdentity ImmutableArray = new(typeof(ImmutableArray<>)); + public const string Namespace = "System.Collections.Immutable"; /// - /// . + /// The generic System.Collections.Immutable.ImmutableArray{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ImmutableList = new(typeof(ImmutableList<>)); + public static readonly TypeIdentity ImmutableArray = new(nameof(ImmutableArray), Namespace, 1); /// - /// . + /// The generic System.Collections.Immutable.ImmutableArray{T}.Builder type. /// - public static readonly TypeIdentity ImmutableDictionary = new(typeof(ImmutableDictionary<,>)); + public static readonly TypeIdentity ImmutableArrayBuilder = new(typeof(ImmutableArray<>.Builder)); /// - /// . + /// The generic System.Collections.Immutable.ImmutableList{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ImmutableHashSet = new(typeof(ImmutableHashSet<>)); + public static readonly TypeIdentity ImmutableList = new(nameof(ImmutableList), Namespace, 1); /// - /// . + /// The generic + /// System.Collections.Immutable.ImmutableDictionary{TKey, TValue} type. + /// The type has arity 2. /// - public static readonly TypeIdentity ImmutableSortedDictionary = new( - typeof(ImmutableSortedDictionary<,>) + public static readonly TypeIdentity ImmutableDictionary = new( + nameof(ImmutableDictionary), + Namespace, + 2 ); /// - /// . - /// - public static readonly TypeIdentity ImmutableSortedSet = new(typeof(ImmutableSortedSet<>)); - - /// - /// . - /// - public static readonly TypeIdentity ImmutableStack = new(typeof(ImmutableStack<>)); - - /// - /// . - /// - public static readonly TypeIdentity ImmutableQueue = new(typeof(ImmutableQueue<>)); - - /// - /// . + /// The generic System.Collections.Immutable.ImmutableHashSet{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity IImmutableList = new(typeof(IImmutableList<>)); + public static readonly TypeIdentity ImmutableHashSet = new(nameof(ImmutableHashSet), Namespace, 1); /// - /// . + /// The generic + /// System.Collections.Immutable.ImmutableSortedDictionary{TKey, TValue} type. + /// The type has arity 2. /// - public static readonly TypeIdentity IImmutableDictionary = new(typeof(IImmutableDictionary<,>)); - - /// - /// . - /// - public static readonly TypeIdentity IImmutableSet = new(typeof(IImmutableSet<>)); + public static readonly TypeIdentity ImmutableSortedDictionary = new( + nameof(ImmutableSortedDictionary), + Namespace, + 2 + ); /// - /// . + /// The generic System.Collections.Immutable.ImmutableSortedSet{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity IImmutableStack = new(typeof(IImmutableStack<>)); + public static readonly TypeIdentity ImmutableSortedSet = new(nameof(ImmutableSortedSet), Namespace, 1); /// - /// . + /// The generic System.Collections.Immutable.ImmutableStack{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity IImmutableQueue = new(typeof(IImmutableQueue<>)); + public static readonly TypeIdentity ImmutableStack = new(nameof(ImmutableStack), Namespace, 1); /// - /// . + /// The generic System.Collections.Immutable.ImmutableQueue{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ImmutableArrayBuilder = new(typeof(ImmutableArray<>.Builder)); + public static readonly TypeIdentity ImmutableQueue = new(nameof(ImmutableQueue), Namespace, 1); /// - /// . + /// The generic System.Collections.Immutable.IImmutableList{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ImmutableListBuilder = new(typeof(ImmutableList<>.Builder)); + public static readonly TypeIdentity IImmutableList = new(nameof(IImmutableList), Namespace, 1); /// - /// . + /// The generic + /// System.Collections.Immutable.IImmutableDictionary{TKey, TValue} type. + /// The type has arity 2. /// - public static readonly TypeIdentity ImmutableDictionaryBuilder = new( - typeof(ImmutableDictionary<,>.Builder) + public static readonly TypeIdentity IImmutableDictionary = new( + nameof(IImmutableDictionary), + Namespace, + 2 ); /// - /// . + /// The generic System.Collections.Immutable.IImmutableSet{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ImmutableHashSetBuilder = new(typeof(ImmutableHashSet<>.Builder)); + public static readonly TypeIdentity IImmutableSet = new(nameof(IImmutableSet), Namespace, 1); /// - /// . + /// The generic System.Collections.Immutable.IImmutableStack{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ImmutableSortedDictionaryBuilder = new( - typeof(ImmutableSortedDictionary<,>.Builder) - ); + public static readonly TypeIdentity IImmutableStack = new(nameof(IImmutableStack), Namespace, 1); /// - /// . + /// The generic System.Collections.Immutable.IImmutableQueue{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity ImmutableSortedSetBuilder = new( - typeof(ImmutableSortedSet<>.Builder) - ); + public static readonly TypeIdentity IImmutableQueue = new(nameof(IImmutableQueue), Namespace, 1); } /// - /// The System.Collections.Frozen namespace. + /// Common types from the System.Collections.Frozen namespace. /// public static partial class Frozen { @@ -659,148 +952,1682 @@ public static partial class Frozen public const string Namespace = "System.Collections.Frozen"; /// - /// . + /// The generic System.Collections.Frozen.FrozenSet{T} type. + /// The type has arity 1. /// - public static readonly TypeIdentity FrozenSet = new( - typeof(global::System.Collections.Frozen.FrozenSet<>) - ); + public static readonly TypeIdentity FrozenSet = new(nameof(FrozenSet), Namespace, 1); /// - /// . + /// The generic System.Collections.Frozen.FrozenDictionary{TKey, TValue} type. + /// The type has arity 2. /// - public static readonly TypeIdentity FrozenDictionary = new( - typeof(global::System.Collections.Frozen.FrozenDictionary<,>) - ); + public static readonly TypeIdentity FrozenDictionary = new(nameof(FrozenDictionary), Namespace, 2); } } /// - /// The System.Diagnostics namespace. + /// Common types from the System.Linq namespace. /// - public static partial class Diagnostics + public static partial class Linq { /// - /// The System.Diagnostics namespace. + /// The System.Linq namespace. /// - public const string Namespace = "System.Diagnostics"; + public const string Namespace = "System.Linq"; /// - /// The System.Diagnostics.CodeAnalysis namespace. + /// The System.Linq.Enumerable type. /// - public static partial class CodeAnalysis - { - /// - /// The System.Diagnostics.CodeAnalysis namespace. - /// - public const string Namespace = "System.Diagnostics.CodeAnalysis"; - - /// - /// . - /// - public static readonly TypeIdentity SuppressMessageAttribute = new( - nameof(SuppressMessageAttribute), - Namespace - ); - - /// - /// The System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute type. - /// - public static readonly TypeIdentity ExcludeFromCodeCoverageAttribute = new( - nameof(ExcludeFromCodeCoverageAttribute), - Namespace - ); - } - } + public static readonly TypeIdentity Enumerable = new(nameof(Enumerable), Namespace); - /// - /// The System.Runtime namespace. - /// - public static partial class Runtime - { /// - /// The System.Runtime namespace. + /// The System.Linq.Queryable type. /// - public const string Namespace = "System.Runtime"; + public static readonly TypeIdentity Queryable = new(nameof(Queryable), Namespace); /// - /// The System.Runtime.CompilerServices namespace. + /// The generic System.Linq.IQueryable{T} type. + /// The type has arity 1. /// - public static partial class CompilerServices - { - /// - /// The System.Runtime.CompilerServices namespace. - /// - public const string Namespace = "System.Runtime.CompilerServices"; + public static readonly TypeIdentity IQueryable = new(nameof(IQueryable), Namespace, 1); - /// - /// . - /// - public static readonly TypeIdentity CompilerGeneratedAttribute = new( - nameof(CompilerGeneratedAttribute), - Namespace - ); - } - } + /// + /// The generic System.Linq.IOrderedQueryable{T} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity IOrderedQueryable = new(nameof(IOrderedQueryable), Namespace, 1); - /// - /// The System.CodeDom namespace. - /// - public static partial class CodeDom - { /// - /// The System.CodeDom namespace. + /// The generic System.Linq.IGrouping{TKey, TElement} type. + /// The type has arity 2. /// - public const string Namespace = "System.CodeDom"; + public static readonly TypeIdentity IGrouping = new(nameof(IGrouping), Namespace, 2); /// - /// The System.CodeDom.Compiler namespace. + /// The generic System.Linq.ILookup{TKey, TElement} type. + /// The type has arity 2. /// - public static partial class Compiler - { - /// - /// The System.CodeDom.Compiler namespace. - /// - public const string Namespace = "System.CodeDom.Compiler"; + public static readonly TypeIdentity ILookup = new(nameof(ILookup), Namespace, 2); - /// - /// . - /// - public static readonly TypeIdentity GeneratedCodeAttribute = new( - nameof(GeneratedCodeAttribute), - Namespace - ); - } + /// + /// The generic System.Linq.IOrderedEnumerable{TElement} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity IOrderedEnumerable = new(nameof(IOrderedEnumerable), Namespace, 1); } - } - - /// - /// Common types from the Microsoft namespace hierarchy. - /// - public static class Microsoft - { - /// - /// The Microsoft namespace. - /// - public const string Namespace = "Microsoft"; /// - /// Common types from the Microsoft.CodeAnalysis namespace. + /// Common types from the System.IO namespace. /// - public static class CodeAnalysis + public static partial class IO { /// - /// The Microsoft.CodeAnalysis namespace. + /// The System.IO namespace. /// - public const string Namespace = "Microsoft.CodeAnalysis"; + public const string Namespace = "System.IO"; /// - /// . + /// The System.IO.Stream type. + /// + public static readonly TypeIdentity Stream = TypeIdentity.Create(); + + /// + /// The System.IO.MemoryStream type. + /// + public static readonly TypeIdentity MemoryStream = TypeIdentity.Create(); + + /// + /// The System.IO.TextReader type. + /// + public static readonly TypeIdentity TextReader = TypeIdentity.Create(); + + /// + /// The System.IO.TextWriter type. + /// + public static readonly TypeIdentity TextWriter = TypeIdentity.Create(); + + /// + /// The System.IO.StringReader type. + /// + public static readonly TypeIdentity StringReader = TypeIdentity.Create(); + + /// + /// The System.IO.StringWriter type. + /// + public static readonly TypeIdentity StringWriter = TypeIdentity.Create(); + + /// + /// The System.IO.StreamReader type. + /// + public static readonly TypeIdentity StreamReader = TypeIdentity.Create(); + + /// + /// The System.IO.StreamWriter type. + /// + public static readonly TypeIdentity StreamWriter = TypeIdentity.Create(); + + /// + /// The System.IO.BinaryReader type. + /// + public static readonly TypeIdentity BinaryReader = TypeIdentity.Create(); + + /// + /// The System.IO.BinaryWriter type. + /// + public static readonly TypeIdentity BinaryWriter = TypeIdentity.Create(); + + /// + /// The System.IO.IOException type. + /// + public static readonly TypeIdentity IOException = TypeIdentity.Create(); + + /// + /// The System.IO.FileInfo type. + /// + public static readonly TypeIdentity FileInfo = TypeIdentity.Create(); + + /// + /// The System.IO.DirectoryInfo type. + /// + public static readonly TypeIdentity DirectoryInfo = TypeIdentity.Create(); + + /// + /// The System.IO.File type. + /// + public static readonly TypeIdentity File = new(nameof(File), Namespace); + + /// + /// The System.IO.Directory type. + /// + public static readonly TypeIdentity Directory = new(nameof(Directory), Namespace); + + /// + /// The System.IO.Path type. + /// + public static readonly TypeIdentity Path = new(nameof(Path), Namespace); + } + + /// + /// Common types from the System.Reflection namespace. + /// + public static partial class Reflection + { + /// + /// The System.Reflection namespace. + /// + public const string Namespace = "System.Reflection"; + + /// + /// The System.Reflection.Assembly type. + /// + public static readonly TypeIdentity Assembly = TypeIdentity.Create(); + + /// + /// The System.Reflection.MemberInfo type. + /// + public static readonly TypeIdentity MemberInfo = + TypeIdentity.Create(); + + /// + /// The System.Reflection.MethodInfo type. + /// + public static readonly TypeIdentity MethodInfo = + TypeIdentity.Create(); + + /// + /// The System.Reflection.PropertyInfo type. + /// + public static readonly TypeIdentity PropertyInfo = + TypeIdentity.Create(); + + /// + /// The System.Reflection.FieldInfo type. + /// + public static readonly TypeIdentity FieldInfo = TypeIdentity.Create(); + + /// + /// The System.Reflection.ConstructorInfo type. + /// + public static readonly TypeIdentity ConstructorInfo = + TypeIdentity.Create(); + + /// + /// The System.Reflection.ParameterInfo type. + /// + public static readonly TypeIdentity ParameterInfo = + TypeIdentity.Create(); + + /// + /// The System.Reflection.BindingFlags type. + /// + public static readonly TypeIdentity BindingFlags = + TypeIdentity.Create(); + + /// + /// The System.Reflection.TypeInfo type. + /// + public static readonly TypeIdentity TypeInfo = TypeIdentity.Create(); + + /// + /// The System.Reflection.EventInfo type. + /// + public static readonly TypeIdentity EventInfo = TypeIdentity.Create(); + + /// + /// The System.Reflection.CustomAttributeData type. + /// + public static readonly TypeIdentity CustomAttributeData = + TypeIdentity.Create(); + + /// + /// The System.Reflection.AssemblyMetadataAttribute type. + /// + public static readonly TypeIdentity AssemblyMetadataAttribute = + TypeIdentity.Create(); + + /// + /// The System.Reflection.AssemblyName type. + /// + public static readonly TypeIdentity AssemblyName = + TypeIdentity.Create(); + } + + /// + /// Common types from the System.Diagnostics namespace hierarchy. + /// + public static partial class Diagnostics + { + /// + /// The System.Diagnostics namespace. + /// + public const string Namespace = "System.Diagnostics"; + + /// + /// The System.Diagnostics.Debugger type. + /// + public static readonly TypeIdentity Debugger = new(nameof(Debugger), Namespace); + + /// + /// The System.Diagnostics.Debug type. + /// + public static readonly TypeIdentity Debug = new(nameof(Debug), Namespace); + + /// + /// The System.Diagnostics.ConditionalAttribute type. + /// + public static readonly TypeIdentity ConditionalAttribute = + TypeIdentity.Create(); + + /// + /// Common types from the System.Diagnostics.CodeAnalysis namespace. + /// + public static partial class CodeAnalysis + { + /// + /// The System.Diagnostics.CodeAnalysis namespace. + /// + public const string Namespace = "System.Diagnostics.CodeAnalysis"; + + /// + /// The System.Diagnostics.CodeAnalysis.SuppressMessageAttribute type. + /// + public static readonly TypeIdentity SuppressMessageAttribute = new( + nameof(SuppressMessageAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute type. + /// + public static readonly TypeIdentity ExcludeFromCodeCoverageAttribute = new( + nameof(ExcludeFromCodeCoverageAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.AllowNullAttribute type. + /// + public static readonly TypeIdentity AllowNullAttribute = new(nameof(AllowNullAttribute), Namespace); + + /// + /// The System.Diagnostics.CodeAnalysis.DisallowNullAttribute type. + /// + public static readonly TypeIdentity DisallowNullAttribute = new( + nameof(DisallowNullAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.MaybeNullAttribute type. + /// + public static readonly TypeIdentity MaybeNullAttribute = new(nameof(MaybeNullAttribute), Namespace); + + /// + /// The System.Diagnostics.CodeAnalysis.NotNullAttribute type. + /// + public static readonly TypeIdentity NotNullAttribute = new(nameof(NotNullAttribute), Namespace); + + /// + /// The System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute type. + /// + public static readonly TypeIdentity MaybeNullWhenAttribute = new( + nameof(MaybeNullWhenAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.NotNullWhenAttribute type. + /// + public static readonly TypeIdentity NotNullWhenAttribute = new(nameof(NotNullWhenAttribute), Namespace); + + /// + /// The System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute type. + /// + public static readonly TypeIdentity NotNullIfNotNullAttribute = new( + nameof(NotNullIfNotNullAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.MemberNotNullAttribute type. + /// + public static readonly TypeIdentity MemberNotNullAttribute = new( + nameof(MemberNotNullAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute type. + /// + public static readonly TypeIdentity MemberNotNullWhenAttribute = new( + nameof(MemberNotNullWhenAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute type. + /// + public static readonly TypeIdentity DoesNotReturnAttribute = new( + nameof(DoesNotReturnAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute type. + /// + public static readonly TypeIdentity DoesNotReturnIfAttribute = new( + nameof(DoesNotReturnIfAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute type. + /// + public static readonly TypeIdentity UnconditionalSuppressMessageAttribute = new( + nameof(UnconditionalSuppressMessageAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute type. + /// + public static readonly TypeIdentity DynamicallyAccessedMembersAttribute = new( + nameof(DynamicallyAccessedMembersAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes type. + /// + public static readonly TypeIdentity DynamicallyAccessedMemberTypes = new( + nameof(DynamicallyAccessedMemberTypes), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute type. + /// + public static readonly TypeIdentity RequiresUnreferencedCodeAttribute = new( + nameof(RequiresUnreferencedCodeAttribute), + Namespace + ); + + /// + /// The System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute type. + /// + public static readonly TypeIdentity SetsRequiredMembersAttribute = new( + nameof(SetsRequiredMembersAttribute), + Namespace + ); + } + } + + /// + /// Common types from the System.Runtime namespace hierarchy. + /// + public static partial class Runtime + { + /// + /// The System.Runtime namespace. + /// + public const string Namespace = "System.Runtime"; + + /// + /// Common types from the System.Runtime.CompilerServices namespace. + /// + public static partial class CompilerServices + { + /// + /// The System.Runtime.CompilerServices namespace. + /// + public const string Namespace = "System.Runtime.CompilerServices"; + + /// + /// The System.Runtime.CompilerServices.CompilerGeneratedAttribute type. + /// + public static readonly TypeIdentity CompilerGeneratedAttribute = new( + nameof(CompilerGeneratedAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.InternalsVisibleToAttribute type. + /// + public static readonly TypeIdentity InternalsVisibleToAttribute = new( + nameof(InternalsVisibleToAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.MethodImplAttribute type. + /// + public static readonly TypeIdentity MethodImplAttribute = new(nameof(MethodImplAttribute), Namespace); + + /// + /// The System.Runtime.CompilerServices.ExtensionAttribute type. + /// + public static readonly TypeIdentity ExtensionAttribute = new(nameof(ExtensionAttribute), Namespace); + + /// + /// The System.Runtime.CompilerServices.CallerMemberNameAttribute type. + /// + public static readonly TypeIdentity CallerMemberNameAttribute = new( + nameof(CallerMemberNameAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.CallerFilePathAttribute type. + /// + public static readonly TypeIdentity CallerFilePathAttribute = new( + nameof(CallerFilePathAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.CallerLineNumberAttribute type. + /// + public static readonly TypeIdentity CallerLineNumberAttribute = new( + nameof(CallerLineNumberAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.CallerArgumentExpressionAttribute type. + /// + public static readonly TypeIdentity CallerArgumentExpressionAttribute = new( + nameof(CallerArgumentExpressionAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.IsExternalInit type. + /// + public static readonly TypeIdentity IsExternalInit = new(nameof(IsExternalInit), Namespace); + + /// + /// The System.Runtime.CompilerServices.RequiredMemberAttribute type. + /// + public static readonly TypeIdentity RequiredMemberAttribute = new( + nameof(RequiredMemberAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.ModuleInitializerAttribute type. + /// + public static readonly TypeIdentity ModuleInitializerAttribute = new( + nameof(ModuleInitializerAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.SkipLocalsInitAttribute type. + /// + public static readonly TypeIdentity SkipLocalsInitAttribute = new( + nameof(SkipLocalsInitAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.EnumeratorCancellationAttribute type. + /// + public static readonly TypeIdentity EnumeratorCancellationAttribute = new( + nameof(EnumeratorCancellationAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute type. + /// + public static readonly TypeIdentity InterpolatedStringHandlerAttribute = new( + nameof(InterpolatedStringHandlerAttribute), + Namespace + ); + + /// + /// The + /// System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute + /// type. + /// + public static readonly TypeIdentity InterpolatedStringHandlerArgumentAttribute = new( + nameof(InterpolatedStringHandlerArgumentAttribute), + Namespace + ); + + /// + /// The System.Runtime.CompilerServices.CollectionBuilderAttribute type. + /// + public static readonly TypeIdentity CollectionBuilderAttribute = new( + nameof(CollectionBuilderAttribute), + Namespace + ); + } + } + + /// + /// Common types from the System.CodeDom namespace hierarchy. + /// + public static partial class CodeDom + { + /// + /// The System.CodeDom namespace. + /// + public const string Namespace = "System.CodeDom"; + + /// + /// Common types from the System.CodeDom.Compiler namespace. + /// + public static partial class Compiler + { + /// + /// The System.CodeDom.Compiler namespace. + /// + public const string Namespace = "System.CodeDom.Compiler"; + + /// + /// The System.CodeDom.Compiler.GeneratedCodeAttribute type. + /// + public static readonly TypeIdentity GeneratedCodeAttribute = new( + nameof(GeneratedCodeAttribute), + Namespace + ); + } + } + + /// + /// Common types from the System.ComponentModel namespace hierarchy. + /// + public static partial class ComponentModel + { + /// + /// The System.ComponentModel namespace. + /// + public const string Namespace = "System.ComponentModel"; + + /// + /// The System.ComponentModel.EditorBrowsableAttribute type. + /// + public static readonly TypeIdentity EditorBrowsableAttribute = new( + nameof(EditorBrowsableAttribute), + Namespace + ); + + /// + /// The System.ComponentModel.EditorBrowsableState type. + /// + public static readonly TypeIdentity EditorBrowsableState = new(nameof(EditorBrowsableState), Namespace); + + /// + /// The System.ComponentModel.DescriptionAttribute type. + /// + public static readonly TypeIdentity DescriptionAttribute = new(nameof(DescriptionAttribute), Namespace); + + /// + /// The System.ComponentModel.DefaultValueAttribute type. + /// + public static readonly TypeIdentity DefaultValueAttribute = new(nameof(DefaultValueAttribute), Namespace); + + /// + /// Common types from the System.ComponentModel.DataAnnotations namespace. + /// + public static partial class DataAnnotations + { + /// + /// The System.ComponentModel.DataAnnotations namespace. + /// + public const string Namespace = "System.ComponentModel.DataAnnotations"; + + /// + /// The System.ComponentModel.DataAnnotations.ValidationAttribute type. + /// + public static readonly TypeIdentity ValidationAttribute = new(nameof(ValidationAttribute), Namespace); + + /// + /// The System.ComponentModel.DataAnnotations.RequiredAttribute type. + /// + public static readonly TypeIdentity RequiredAttribute = new(nameof(RequiredAttribute), Namespace); + + /// + /// The System.ComponentModel.DataAnnotations.RangeAttribute type. + /// + public static readonly TypeIdentity RangeAttribute = new(nameof(RangeAttribute), Namespace); + + /// + /// The System.ComponentModel.DataAnnotations.RegularExpressionAttribute type. + /// + public static readonly TypeIdentity RegularExpressionAttribute = new( + nameof(RegularExpressionAttribute), + Namespace + ); + + /// + /// The System.ComponentModel.DataAnnotations.StringLengthAttribute type. + /// + public static readonly TypeIdentity StringLengthAttribute = new( + nameof(StringLengthAttribute), + Namespace + ); + + /// + /// The System.ComponentModel.DataAnnotations.MinLengthAttribute type. + /// + public static readonly TypeIdentity MinLengthAttribute = new(nameof(MinLengthAttribute), Namespace); + + /// + /// The System.ComponentModel.DataAnnotations.MaxLengthAttribute type. + /// + public static readonly TypeIdentity MaxLengthAttribute = new(nameof(MaxLengthAttribute), Namespace); + + /// + /// The System.ComponentModel.DataAnnotations.CompareAttribute type. + /// + public static readonly TypeIdentity CompareAttribute = new(nameof(CompareAttribute), Namespace); + + /// + /// The System.ComponentModel.DataAnnotations.DisplayAttribute type. + /// + public static readonly TypeIdentity DisplayAttribute = new(nameof(DisplayAttribute), Namespace); + + /// + /// The System.ComponentModel.DataAnnotations.DataTypeAttribute type. + /// + public static readonly TypeIdentity DataTypeAttribute = new(nameof(DataTypeAttribute), Namespace); + + /// + /// The System.ComponentModel.DataAnnotations.EnumDataTypeAttribute type. + /// + public static readonly TypeIdentity EnumDataTypeAttribute = new( + nameof(EnumDataTypeAttribute), + Namespace + ); + + /// + /// The System.ComponentModel.DataAnnotations.CustomValidationAttribute type. + /// + public static readonly TypeIdentity CustomValidationAttribute = new( + nameof(CustomValidationAttribute), + Namespace + ); + + /// + /// The System.ComponentModel.DataAnnotations.EmailAddressAttribute type. + /// + public static readonly TypeIdentity EmailAddressAttribute = new( + nameof(EmailAddressAttribute), + Namespace + ); + } + } + + /// + /// Common types from the System.Text namespace hierarchy. + /// + public static partial class Text + { + /// + /// The System.Text namespace. + /// + public const string Namespace = "System.Text"; + + /// + /// The System.Text.Encoding type. + /// + public static readonly TypeIdentity Encoding = new(nameof(Encoding), Namespace); + + /// + /// The System.Text.Encoder type. + /// + public static readonly TypeIdentity Encoder = new(nameof(Encoder), Namespace); + + /// + /// The System.Text.Decoder type. + /// + public static readonly TypeIdentity Decoder = new(nameof(Decoder), Namespace); + + /// + /// The System.Text.StringBuilder type. + /// + public static readonly TypeIdentity StringBuilder = new(nameof(StringBuilder), Namespace); + + /// + /// The System.Text.Rune type. + /// + public static readonly TypeIdentity Rune = new(nameof(Rune), Namespace); + + /// + /// The System.Text.NormalizationForm type. + /// + public static readonly TypeIdentity NormalizationForm = new(nameof(NormalizationForm), Namespace); + + /// + /// The System.Text.UTF8Encoding type. + /// + public static readonly TypeIdentity UTF8Encoding = TypeIdentity.Create(); + + /// + /// The System.Text.UnicodeEncoding type. + /// + public static readonly TypeIdentity UnicodeEncoding = + TypeIdentity.Create(); + + /// + /// The System.Text.ASCIIEncoding type. + /// + public static readonly TypeIdentity ASCIIEncoding = + TypeIdentity.Create(); + + /// + /// The System.Text.EncoderFallback type. + /// + public static readonly TypeIdentity EncoderFallback = new(nameof(EncoderFallback), Namespace); + + /// + /// The System.Text.DecoderFallback type. + /// + public static readonly TypeIdentity DecoderFallback = new(nameof(DecoderFallback), Namespace); + + /// + /// The System.Text.EncoderFallbackException type. + /// + public static readonly TypeIdentity EncoderFallbackException = new( + nameof(EncoderFallbackException), + Namespace + ); + + /// + /// The System.Text.DecoderFallbackException type. + /// + public static readonly TypeIdentity DecoderFallbackException = new( + nameof(DecoderFallbackException), + Namespace + ); + + /// + /// Common types from the System.Text.RegularExpressions namespace. + /// + public static partial class RegularExpressions + { + /// + /// The System.Text.RegularExpressions namespace. + /// + public const string Namespace = "System.Text.RegularExpressions"; + + /// + /// The System.Text.RegularExpressions.Regex type. + /// + public static readonly TypeIdentity Regex = + TypeIdentity.Create(); + + /// + /// The System.Text.RegularExpressions.RegexOptions type. + /// + public static readonly TypeIdentity RegexOptions = + TypeIdentity.Create(); + + /// + /// The System.Text.RegularExpressions.Match type. + /// + public static readonly TypeIdentity Match = + TypeIdentity.Create(); + + /// + /// The System.Text.RegularExpressions.Group type. + /// + public static readonly TypeIdentity Group = + TypeIdentity.Create(); + + /// + /// The System.Text.RegularExpressions.Capture type. + /// + public static readonly TypeIdentity Capture = + TypeIdentity.Create(); + + /// + /// The System.Text.RegularExpressions.MatchCollection type. + /// + public static readonly TypeIdentity MatchCollection = + TypeIdentity.Create(); + + /// + /// The System.Text.RegularExpressions.GroupCollection type. + /// + public static readonly TypeIdentity GroupCollection = + TypeIdentity.Create(); + + /// + /// The System.Text.RegularExpressions.CaptureCollection type. + /// + public static readonly TypeIdentity CaptureCollection = + TypeIdentity.Create(); + } + + /// + /// Common types from the System.Text.Json namespace hierarchy. + /// + public static partial class Json + { + /// + /// The System.Text.Json namespace. + /// + public const string Namespace = "System.Text.Json"; + + /// + /// The System.Text.Json.JsonSerializer type. + /// + public static readonly TypeIdentity JsonSerializer = new(nameof(JsonSerializer), Namespace); + + /// + /// The System.Text.Json.JsonException type. + /// + public static readonly TypeIdentity JsonException = new(nameof(JsonException), Namespace); + + /// + /// The System.Text.Json.JsonSerializerOptions type. + /// + public static readonly TypeIdentity JsonSerializerOptions = new( + nameof(JsonSerializerOptions), + Namespace + ); + + /// + /// The System.Text.Json.JsonSerializerDefaults type. + /// + public static readonly TypeIdentity JsonSerializerDefaults = new( + nameof(JsonSerializerDefaults), + Namespace + ); + + /// + /// The System.Text.Json.JsonDocument type. + /// + public static readonly TypeIdentity JsonDocument = new(nameof(JsonDocument), Namespace); + + /// + /// The System.Text.Json.JsonDocumentOptions type. + /// + public static readonly TypeIdentity JsonDocumentOptions = new(nameof(JsonDocumentOptions), Namespace); + + /// + /// The System.Text.Json.JsonElement type. + /// + public static readonly TypeIdentity JsonElement = new(nameof(JsonElement), Namespace); + + /// + /// The System.Text.Json.JsonProperty type. + /// + public static readonly TypeIdentity JsonProperty = new(nameof(JsonProperty), Namespace); + + /// + /// The System.Text.Json.JsonValueKind type. + /// + public static readonly TypeIdentity JsonValueKind = new(nameof(JsonValueKind), Namespace); + + /// + /// The System.Text.Json.JsonTokenType type. + /// + public static readonly TypeIdentity JsonTokenType = new(nameof(JsonTokenType), Namespace); + + /// + /// The System.Text.Json.JsonCommentHandling type. + /// + public static readonly TypeIdentity JsonCommentHandling = new(nameof(JsonCommentHandling), Namespace); + + /// + /// The System.Text.Json.JsonNamingPolicy type. + /// + public static readonly TypeIdentity JsonNamingPolicy = new(nameof(JsonNamingPolicy), Namespace); + + /// + /// The System.Text.Json.JsonReaderOptions type. + /// + public static readonly TypeIdentity JsonReaderOptions = new(nameof(JsonReaderOptions), Namespace); + + /// + /// The System.Text.Json.JsonWriterOptions type. + /// + public static readonly TypeIdentity JsonWriterOptions = new(nameof(JsonWriterOptions), Namespace); + + /// + /// The System.Text.Json.Utf8JsonReader type. + /// + public static readonly TypeIdentity Utf8JsonReader = new(nameof(Utf8JsonReader), Namespace); + + /// + /// The System.Text.Json.Utf8JsonWriter type. + /// + public static readonly TypeIdentity Utf8JsonWriter = new(nameof(Utf8JsonWriter), Namespace); + + /// + /// The System.Text.Json.JsonEncodedText type. + /// + public static readonly TypeIdentity JsonEncodedText = new(nameof(JsonEncodedText), Namespace); + + /// + /// The System.Text.Json.JsonReaderState type. + /// + public static readonly TypeIdentity JsonReaderState = new(nameof(JsonReaderState), Namespace); + + /// + /// Common types from the System.Text.Json.Nodes namespace. + /// + public static partial class Nodes + { + /// + /// The System.Text.Json.Nodes namespace. + /// + public const string Namespace = "System.Text.Json.Nodes"; + + /// + /// The System.Text.Json.Nodes.JsonNode type. + /// + public static readonly TypeIdentity JsonNode = new(nameof(JsonNode), Namespace); + + /// + /// The System.Text.Json.Nodes.JsonObject type. + /// + public static readonly TypeIdentity JsonObject = new(nameof(JsonObject), Namespace); + + /// + /// The System.Text.Json.Nodes.JsonArray type. + /// + public static readonly TypeIdentity JsonArray = new(nameof(JsonArray), Namespace); + + /// + /// The System.Text.Json.Nodes.JsonValue type. + /// + public static readonly TypeIdentity JsonValue = new(nameof(JsonValue), Namespace); + + /// + /// The System.Text.Json.Nodes.JsonNodeOptions type. + /// + public static readonly TypeIdentity JsonNodeOptions = new(nameof(JsonNodeOptions), Namespace); + } + + /// + /// Common types from the System.Text.Json.Serialization namespace hierarchy. + /// + public static partial class Serialization + { + /// + /// The System.Text.Json.Serialization namespace. + /// + public const string Namespace = "System.Text.Json.Serialization"; + + /// + /// The System.Text.Json.Serialization.JsonIgnoreAttribute type. + /// + public static readonly TypeIdentity JsonIgnoreAttribute = new( + nameof(JsonIgnoreAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonPropertyNameAttribute type. + /// + public static readonly TypeIdentity JsonPropertyNameAttribute = new( + nameof(JsonPropertyNameAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonIncludeAttribute type. + /// + public static readonly TypeIdentity JsonIncludeAttribute = new( + nameof(JsonIncludeAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonConstructorAttribute type. + /// + public static readonly TypeIdentity JsonConstructorAttribute = new( + nameof(JsonConstructorAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonExtensionDataAttribute type. + /// + public static readonly TypeIdentity JsonExtensionDataAttribute = new( + nameof(JsonExtensionDataAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonNumberHandlingAttribute type. + /// + public static readonly TypeIdentity JsonNumberHandlingAttribute = new( + nameof(JsonNumberHandlingAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonConverterAttribute type. + /// + public static readonly TypeIdentity JsonConverterAttribute = new( + nameof(JsonConverterAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonRequiredAttribute type. + /// + public static readonly TypeIdentity JsonRequiredAttribute = new( + nameof(JsonRequiredAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonDerivedTypeAttribute type. + /// + public static readonly TypeIdentity JsonDerivedTypeAttribute = new( + nameof(JsonDerivedTypeAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonPolymorphicAttribute type. + /// + public static readonly TypeIdentity JsonPolymorphicAttribute = new( + nameof(JsonPolymorphicAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonPropertyOrderAttribute type. + /// + public static readonly TypeIdentity JsonPropertyOrderAttribute = new( + nameof(JsonPropertyOrderAttribute), + Namespace + ); + + /// + /// The + /// System.Text.Json.Serialization.JsonUnmappedMemberHandlingAttribute + /// type. + /// + public static readonly TypeIdentity JsonUnmappedMemberHandlingAttribute = new( + nameof(JsonUnmappedMemberHandlingAttribute), + Namespace + ); + + /// + /// The + /// System.Text.Json.Serialization.JsonObjectCreationHandlingAttribute + /// type. + /// + public static readonly TypeIdentity JsonObjectCreationHandlingAttribute = new( + nameof(JsonObjectCreationHandlingAttribute), + Namespace + ); + + /// + /// The + /// System.Text.Json.Serialization.JsonStringEnumMemberNameAttribute + /// type. + /// + public static readonly TypeIdentity JsonStringEnumMemberNameAttribute = new( + nameof(JsonStringEnumMemberNameAttribute), + Namespace + ); + + /// + /// The non-generic System.Text.Json.Serialization.JsonConverter type. + /// + /// + /// The generic System.Text.Json.Serialization.JsonConverter{T} type + /// has arity 1 and can be represented by applying that arity to this identity. + /// + public static readonly TypeIdentity JsonConverter = new(nameof(JsonConverter), Namespace, 1); + + /// + /// The System.Text.Json.Serialization.JsonConverterFactory type. + /// + public static readonly TypeIdentity JsonConverterFactory = new( + nameof(JsonConverterFactory), + Namespace, + 1 + ); + + /// + /// The System.Text.Json.Serialization.JsonStringEnumConverter type. + /// The generic JsonStringEnumConverter{TEnum} form has arity 1. + /// + public static readonly TypeIdentity JsonStringEnumConverter = new( + nameof(JsonStringEnumConverter), + Namespace, + 1 + ); + + /// + /// The System.Text.Json.Serialization.JsonIgnoreCondition type. + /// + public static readonly TypeIdentity JsonIgnoreCondition = new( + nameof(JsonIgnoreCondition), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonNumberHandling type. + /// + public static readonly TypeIdentity JsonNumberHandling = new(nameof(JsonNumberHandling), Namespace); + + /// + /// The System.Text.Json.Serialization.JsonKnownNamingPolicy type. + /// + public static readonly TypeIdentity JsonKnownNamingPolicy = new( + nameof(JsonKnownNamingPolicy), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonUnknownTypeHandling type. + /// + public static readonly TypeIdentity JsonUnknownTypeHandling = new( + nameof(JsonUnknownTypeHandling), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonUnmappedMemberHandling type. + /// + public static readonly TypeIdentity JsonUnmappedMemberHandling = new( + nameof(JsonUnmappedMemberHandling), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonUnknownDerivedTypeHandling type. + /// + public static readonly TypeIdentity JsonUnknownDerivedTypeHandling = new( + nameof(JsonUnknownDerivedTypeHandling), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonObjectCreationHandling type. + /// + public static readonly TypeIdentity JsonObjectCreationHandling = new( + nameof(JsonObjectCreationHandling), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.ReferenceHandler type. + /// + public static readonly TypeIdentity ReferenceHandler = new(nameof(ReferenceHandler), Namespace); + + /// + /// The System.Text.Json.Serialization.ReferenceResolver type. + /// + public static readonly TypeIdentity ReferenceResolver = new(nameof(ReferenceResolver), Namespace); + + /// + /// The + /// System.Text.Json.Serialization.JsonSourceGenerationOptionsAttribute + /// type. + /// + public static readonly TypeIdentity JsonSourceGenerationOptionsAttribute = new( + nameof(JsonSourceGenerationOptionsAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonSerializableAttribute type. + /// + public static readonly TypeIdentity JsonSerializableAttribute = new( + nameof(JsonSerializableAttribute), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonSourceGenerationMode type. + /// + public static readonly TypeIdentity JsonSourceGenerationMode = new( + nameof(JsonSourceGenerationMode), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.JsonSerializerContext type. + /// + public static readonly TypeIdentity JsonSerializerContext = new( + nameof(JsonSerializerContext), + Namespace + ); + + /// + /// Common types from the + /// System.Text.Json.Serialization.Metadata namespace. + /// + public static partial class Metadata + { + /// + /// The System.Text.Json.Serialization.Metadata namespace. + /// + public const string Namespace = "System.Text.Json.Serialization.Metadata"; + + /// + /// The non-generic + /// System.Text.Json.Serialization.Metadata.JsonTypeInfo type. + /// + /// + /// The generic + /// System.Text.Json.Serialization.Metadata.JsonTypeInfo{T} + /// type has arity 1 and can be represented by applying that arity + /// to this identity. + /// + public static readonly TypeIdentity JsonTypeInfo = new(nameof(JsonTypeInfo), Namespace, 1); + + /// + /// The System.Text.Json.Serialization.Metadata.JsonPropertyInfo + /// type. + /// + public static readonly TypeIdentity JsonPropertyInfo = new(nameof(JsonPropertyInfo), Namespace); + + /// + /// The System.Text.Json.Serialization.Metadata.JsonParameterInfo + /// type. + /// + public static readonly TypeIdentity JsonParameterInfo = new( + nameof(JsonParameterInfo), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.Metadata.JsonTypeInfoKind + /// type. + /// + public static readonly TypeIdentity JsonTypeInfoKind = new(nameof(JsonTypeInfoKind), Namespace); + + /// + /// The + /// System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver + /// type. + /// + public static readonly TypeIdentity IJsonTypeInfoResolver = new( + nameof(IJsonTypeInfoResolver), + Namespace + ); + + /// + /// The + /// System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver + /// type. + /// + public static readonly TypeIdentity DefaultJsonTypeInfoResolver = new( + nameof(DefaultJsonTypeInfoResolver), + Namespace + ); + + /// + /// The + /// System.Text.Json.Serialization.Metadata.JsonPolymorphismOptions + /// type. + /// + public static readonly TypeIdentity JsonPolymorphismOptions = new( + nameof(JsonPolymorphismOptions), + Namespace + ); + + /// + /// The + /// System.Text.Json.Serialization.Metadata.JsonTypeInfoResolver + /// type. + /// + public static readonly TypeIdentity JsonTypeInfoResolver = new( + nameof(JsonTypeInfoResolver), + Namespace + ); + + /// + /// The System.Text.Json.Serialization.Metadata.JsonDerivedType + /// type. + /// + public static readonly TypeIdentity JsonDerivedType = new(nameof(JsonDerivedType), Namespace); + + /// + /// The + /// System.Text.Json.Serialization.Metadata.JsonMetadataServices + /// type. + /// + public static readonly TypeIdentity JsonMetadataServices = new( + nameof(JsonMetadataServices), + Namespace + ); + + /// + /// The generic + /// System.Text.Json.Serialization.Metadata.JsonObjectInfoValues{T} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity JsonObjectInfoValues = new( + nameof(JsonObjectInfoValues), + Namespace + ); + + /// + /// The generic + /// System.Text.Json.Serialization.Metadata.JsonCollectionInfoValues{TCollection} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity JsonCollectionInfoValues = new( + nameof(JsonCollectionInfoValues), + Namespace + ); + + /// + /// The generic + /// System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues{T} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity JsonPropertyInfoValues = new( + nameof(JsonPropertyInfoValues), + Namespace + ); + + /// + /// The + /// System.Text.Json.Serialization.Metadata.JsonParameterInfoValues + /// type. + /// + public static readonly TypeIdentity JsonParameterInfoValues = new( + nameof(JsonParameterInfoValues), + Namespace + ); + } + } + } + } + } + + /// + /// Common types from the Microsoft namespace hierarchy. + /// + public static class Microsoft + { + /// + /// The Microsoft namespace. + /// + public const string Namespace = "Microsoft"; + + /// + /// Common types from the Microsoft.CodeAnalysis namespace hierarchy. + /// + public static class CodeAnalysis + { + /// + /// The Microsoft.CodeAnalysis namespace. + /// + public const string Namespace = "Microsoft.CodeAnalysis"; + + /// + /// The Microsoft.CodeAnalysis.EmbeddedAttribute type. /// public static readonly TypeIdentity EmbeddedAttribute = new(nameof(EmbeddedAttribute), Namespace); + + /// + /// The Microsoft.CodeAnalysis.GeneratorAttribute type. + /// + public static readonly TypeIdentity GeneratorAttribute = new( + nameof(global::Microsoft.CodeAnalysis.GeneratorAttribute), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.ISourceGenerator type. + /// + public static readonly TypeIdentity ISourceGenerator = new( + nameof(global::Microsoft.CodeAnalysis.ISourceGenerator), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.IIncrementalGenerator type. + /// + public static readonly TypeIdentity IIncrementalGenerator = new( + nameof(global::Microsoft.CodeAnalysis.IIncrementalGenerator), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.GeneratorInitializationContext type. + /// + public static readonly TypeIdentity GeneratorInitializationContext = new( + nameof(global::Microsoft.CodeAnalysis.GeneratorInitializationContext), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.GeneratorExecutionContext type. + /// + public static readonly TypeIdentity GeneratorExecutionContext = new( + nameof(global::Microsoft.CodeAnalysis.GeneratorExecutionContext), + Namespace + ); + + /// + /// The + /// Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext + /// type. + /// + public static readonly TypeIdentity IncrementalGeneratorInitializationContext = new( + nameof(global::Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.SourceProductionContext type. + /// + public static readonly TypeIdentity SourceProductionContext = new( + nameof(global::Microsoft.CodeAnalysis.SourceProductionContext), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.Compilation type. + /// + public static readonly TypeIdentity Compilation = new( + nameof(global::Microsoft.CodeAnalysis.Compilation), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.SyntaxTree type. + /// + public static readonly TypeIdentity SyntaxTree = new( + nameof(global::Microsoft.CodeAnalysis.SyntaxTree), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.SyntaxNode type. + /// + public static readonly TypeIdentity SyntaxNode = new( + nameof(global::Microsoft.CodeAnalysis.SyntaxNode), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.SemanticModel type. + /// + public static readonly TypeIdentity SemanticModel = new( + nameof(global::Microsoft.CodeAnalysis.SemanticModel), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.ISymbol type. + /// + public static readonly TypeIdentity ISymbol = new( + nameof(global::Microsoft.CodeAnalysis.ISymbol), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.INamedTypeSymbol type. + /// + public static readonly TypeIdentity INamedTypeSymbol = new( + nameof(global::Microsoft.CodeAnalysis.INamedTypeSymbol), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.ITypeSymbol type. + /// + public static readonly TypeIdentity ITypeSymbol = new( + nameof(global::Microsoft.CodeAnalysis.ITypeSymbol), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.IMethodSymbol type. + /// + public static readonly TypeIdentity IMethodSymbol = new( + nameof(global::Microsoft.CodeAnalysis.IMethodSymbol), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.IPropertySymbol type. + /// + public static readonly TypeIdentity IPropertySymbol = new( + nameof(global::Microsoft.CodeAnalysis.IPropertySymbol), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.IFieldSymbol type. + /// + public static readonly TypeIdentity IFieldSymbol = new( + nameof(global::Microsoft.CodeAnalysis.IFieldSymbol), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.AttributeData type. + /// + public static readonly TypeIdentity AttributeData = new( + nameof(global::Microsoft.CodeAnalysis.AttributeData), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.TypedConstant type. + /// + public static readonly TypeIdentity TypedConstant = new( + nameof(global::Microsoft.CodeAnalysis.TypedConstant), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.Diagnostic type. + /// + public static readonly TypeIdentity Diagnostic = new( + nameof(global::Microsoft.CodeAnalysis.Diagnostic), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.DiagnosticDescriptor type. + /// + public static readonly TypeIdentity DiagnosticDescriptor = new( + nameof(global::Microsoft.CodeAnalysis.DiagnosticDescriptor), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.DiagnosticSeverity type. + /// + public static readonly TypeIdentity DiagnosticSeverity = new( + nameof(global::Microsoft.CodeAnalysis.DiagnosticSeverity), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.Location type. + /// + public static readonly TypeIdentity Location = new( + nameof(global::Microsoft.CodeAnalysis.Location), + Namespace + ); + + /// + /// Common types from the Microsoft.CodeAnalysis.Diagnostics namespace. + /// + public static class Diagnostics + { + /// + /// The Microsoft.CodeAnalysis.Diagnostics namespace. + /// + public const string Namespace = "Microsoft.CodeAnalysis.Diagnostics"; + + /// + /// The Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer type. + /// + public static readonly TypeIdentity DiagnosticAnalyzer = new( + nameof(global::Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer), + Namespace + ); + + /// + /// The + /// Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzerAttribute + /// type. + /// + public static readonly TypeIdentity DiagnosticAnalyzerAttribute = new( + nameof(global::Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzerAttribute), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.Diagnostics.AnalysisContext type. + /// + public static readonly TypeIdentity AnalysisContext = new( + nameof(global::Microsoft.CodeAnalysis.Diagnostics.AnalysisContext), + Namespace + ); + } + + /// + /// Common types from the Microsoft.CodeAnalysis.CSharp namespace. + /// + public static class CSharp + { + /// + /// The Microsoft.CodeAnalysis.CSharp namespace. + /// + public const string Namespace = "Microsoft.CodeAnalysis.CSharp"; + + /// + /// The Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree type. + /// + public static readonly TypeIdentity CSharpSyntaxTree = new( + nameof(global::Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.CSharp.CSharpCompilation type. + /// + public static readonly TypeIdentity CSharpCompilation = new( + nameof(global::Microsoft.CodeAnalysis.CSharp.CSharpCompilation), + Namespace + ); + + /// + /// The Microsoft.CodeAnalysis.CSharp.SyntaxKind type. + /// + public static readonly TypeIdentity SyntaxKind = new( + nameof(global::Microsoft.CodeAnalysis.CSharp.SyntaxKind), + Namespace + ); + } } /// - /// Common types from the Microsoft.Extensions namespace. + /// Common types from the Microsoft.Extensions namespace hierarchy. /// public static partial class Extensions { @@ -817,31 +2644,233 @@ public static class DependencyInjection /// /// The Microsoft.Extensions.DependencyInjection namespace. /// - public const string DependencyInjectionNamespace = "Microsoft.Extensions.DependencyInjection"; + public const string Namespace = "Microsoft.Extensions.DependencyInjection"; + + /// + /// The Microsoft.Extensions.DependencyInjection.IServiceCollection type. + /// + public static readonly TypeIdentity IServiceCollection = new(nameof(IServiceCollection), Namespace); + + /// + /// The Microsoft.Extensions.DependencyInjection.ServiceDescriptor type. + /// + public static readonly TypeIdentity ServiceDescriptor = new(nameof(ServiceDescriptor), Namespace); + + /// + /// The Microsoft.Extensions.DependencyInjection.ServiceLifetime type. + /// + public static readonly TypeIdentity ServiceLifetime = new(nameof(ServiceLifetime), Namespace); + + /// + /// The Microsoft.Extensions.DependencyInjection.ServiceCollection type. + /// + public static readonly TypeIdentity ServiceCollection = new(nameof(ServiceCollection), Namespace); + + /// + /// The + /// Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions + /// type. + /// + public static readonly TypeIdentity ServiceCollectionServiceExtensions = new( + nameof(ServiceCollectionServiceExtensions), + Namespace + ); + + /// + /// The Microsoft.Extensions.DependencyInjection.ActivatorUtilities type. + /// + public static readonly TypeIdentity ActivatorUtilities = new(nameof(ActivatorUtilities), Namespace); + + /// + /// The + /// Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions + /// type. + /// + public static readonly TypeIdentity ServiceProviderServiceExtensions = new( + nameof(ServiceProviderServiceExtensions), + Namespace + ); + } + + /// + /// Common types from the Microsoft.Extensions.Options namespace. + /// + public static class Options + { + /// + /// The Microsoft.Extensions.Options namespace. + /// + public const string Namespace = "Microsoft.Extensions.Options"; + + /// + /// The generic Microsoft.Extensions.Options.IOptions{TOptions} type. + /// The type has arity 1. + /// + public static readonly TypeIdentity IOptions = new(nameof(IOptions), Namespace, 1); + + /// + /// The generic Microsoft.Extensions.Options.IOptionsSnapshot{TOptions} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity IOptionsSnapshot = new(nameof(IOptionsSnapshot), Namespace, 1); + + /// + /// The generic Microsoft.Extensions.Options.IOptionsMonitor{TOptions} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity IOptionsMonitor = new(nameof(IOptionsMonitor), Namespace, 1); + + /// + /// The generic Microsoft.Extensions.Options.IValidateOptions{TOptions} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity IValidateOptions = new(nameof(IValidateOptions), Namespace, 1); + + /// + /// The generic Microsoft.Extensions.Options.IConfigureOptions{TOptions} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity IConfigureOptions = new(nameof(IConfigureOptions), Namespace, 1); + + /// + /// The generic + /// Microsoft.Extensions.Options.IPostConfigureOptions{TOptions} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity IPostConfigureOptions = new( + nameof(IPostConfigureOptions), + Namespace, + 1 + ); + + /// + /// The generic Microsoft.Extensions.Options.OptionsBuilder{TOptions} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity OptionsBuilder = new(nameof(OptionsBuilder), Namespace, 1); + + /// + /// The generic Microsoft.Extensions.Options.OptionsFactory{TOptions} + /// type. The type has arity 1. + /// + public static readonly TypeIdentity OptionsFactory = new(nameof(OptionsFactory), Namespace, 1); + + /// + /// The Microsoft.Extensions.Options.Options type. + /// + /// + /// Named OptionsType to avoid conflicting with the containing + /// Options type-library class. + /// + public static readonly TypeIdentity OptionsType = new(nameof(Options), Namespace); + + /// + /// The Microsoft.Extensions.Options.ValidateOptionsResult type. + /// + public static readonly TypeIdentity ValidateOptionsResult = new( + nameof(ValidateOptionsResult), + Namespace + ); + } + + /// + /// Common types from the Microsoft.Extensions.Logging namespace. + /// + public static class Logging + { + /// + /// The Microsoft.Extensions.Logging namespace. + /// + public const string Namespace = "Microsoft.Extensions.Logging"; + + /// + /// The Microsoft.Extensions.Logging.ILogger type. + /// The generic ILogger{TCategoryName} form has arity 1. + /// + public static readonly TypeIdentity ILogger = new(nameof(ILogger), Namespace, 1); + + /// + /// The Microsoft.Extensions.Logging.ILoggerFactory type. + /// + public static readonly TypeIdentity ILoggerFactory = new(nameof(ILoggerFactory), Namespace); + + /// + /// The Microsoft.Extensions.Logging.ILoggerProvider type. + /// + public static readonly TypeIdentity ILoggerProvider = new(nameof(ILoggerProvider), Namespace); + + /// + /// The Microsoft.Extensions.Logging.EventId type. + /// + public static readonly TypeIdentity EventId = new(nameof(EventId), Namespace); + + /// + /// The Microsoft.Extensions.Logging.LoggerMessage type. + /// + public static readonly TypeIdentity LoggerMessage = new(nameof(LoggerMessage), Namespace); + + /// + /// The Microsoft.Extensions.Logging.LoggerMessageAttribute type. + /// + public static readonly TypeIdentity LoggerMessageAttribute = new( + nameof(LoggerMessageAttribute), + Namespace + ); + + /// + /// The Microsoft.Extensions.Logging.LogLevel type. + /// + public static readonly TypeIdentity LogLevel = new(nameof(LogLevel), Namespace); + } + + /// + /// Common types from the Microsoft.Extensions.Configuration namespace. + /// + public static class Configuration + { + /// + /// The Microsoft.Extensions.Configuration namespace. + /// + public const string Namespace = "Microsoft.Extensions.Configuration"; /// - /// Microsoft.Extensions.DependencyInjection.IServiceCollection + /// The Microsoft.Extensions.Configuration.IConfiguration type. /// - public static readonly TypeIdentity IServiceCollection = new( - nameof(IServiceCollection), - DependencyInjectionNamespace + public static readonly TypeIdentity IConfiguration = new(nameof(IConfiguration), Namespace); + + /// + /// The Microsoft.Extensions.Configuration.IConfigurationSection type. + /// + public static readonly TypeIdentity IConfigurationSection = new( + nameof(IConfigurationSection), + Namespace ); /// - /// Microsoft.Extensions.DependencyInjection.ServiceDescriptor + /// The Microsoft.Extensions.Configuration.IConfigurationBuilder type. /// - public static readonly TypeIdentity ServiceDescriptor = new( - nameof(ServiceDescriptor), - DependencyInjectionNamespace + public static readonly TypeIdentity IConfigurationBuilder = new( + nameof(IConfigurationBuilder), + Namespace ); /// - /// Microsoft.Extensions.DependencyInjection.ServiceLifetime + /// The Microsoft.Extensions.Configuration.IConfigurationProvider type. /// - public static readonly TypeIdentity ServiceLifetime = new( - nameof(ServiceLifetime), - DependencyInjectionNamespace + public static readonly TypeIdentity IConfigurationProvider = new( + nameof(IConfigurationProvider), + Namespace ); + + /// + /// The Microsoft.Extensions.Configuration.ConfigurationBuilder type. + /// + public static readonly TypeIdentity ConfigurationBuilder = new(nameof(ConfigurationBuilder), Namespace); + + /// + /// The Microsoft.Extensions.Configuration.IConfigurationRoot type. + /// + public static readonly TypeIdentity IConfigurationRoot = new(nameof(IConfigurationRoot), Namespace); } } } diff --git a/src/src/SourceGeneratorShared/TypeIdentity.cs b/src/src/SourceGeneratorShared/TypeIdentity.cs index 31435af..c77ab8f 100644 --- a/src/src/SourceGeneratorShared/TypeIdentity.cs +++ b/src/src/SourceGeneratorShared/TypeIdentity.cs @@ -817,6 +817,11 @@ public TypeIdentity MakeGeneric(params TypeReference[] typeArguments) /// public static readonly TypeIdentity Null = new("null", null); + /// + /// Represents the `void` type identity. + /// + public static readonly TypeIdentity Void = new("void", null); + /// /// Creates a from a runtime type. /// diff --git a/src/src/SourceGeneratorShared/TypeLibraryDiagnosticRules.cs b/src/src/SourceGeneratorShared/TypeLibraryDiagnosticRules.cs index 4d9361f..d31ba67 100644 --- a/src/src/SourceGeneratorShared/TypeLibraryDiagnosticRules.cs +++ b/src/src/SourceGeneratorShared/TypeLibraryDiagnosticRules.cs @@ -231,4 +231,16 @@ public static class TypeLibraryDiagnosticRules DiagnosticSeverity.Info, isEnabledByDefault: true ); + + /// + /// Diagnostic raised when an [EnumValues] member references a type that is not an enum. + /// + public static readonly DiagnosticDescriptor EnumValuesTypeNotEnum = new( + "TLB0020", + "Enum values member references a type that is not an enum", + "Enum values member '{0}' references type '{1}' which is not an enum type", + "TypeLibrary", + DiagnosticSeverity.Error, + isEnabledByDefault: true + ); } diff --git a/src/src/SourceGeneratorShared/TypeSyntaxFacts.cs b/src/src/SourceGeneratorShared/TypeSyntaxFacts.cs index feb905b..d99bd4b 100644 --- a/src/src/SourceGeneratorShared/TypeSyntaxFacts.cs +++ b/src/src/SourceGeneratorShared/TypeSyntaxFacts.cs @@ -53,7 +53,7 @@ public static bool TryGetCore(TypeSyntax? typeSyntax, out TypeSyntax core, out I case ArrayTypeSyntax arrayType: { // `int[][,]` parses as a single node with a rank-specifier list, outermost-first. - modifiers ??= [with(arrayType.RankSpecifiers.Count)]; + modifiers ??= new List(arrayType.RankSpecifiers.Count); foreach (var rankSpecifier in arrayType.RankSpecifiers) modifiers.Add(TypeModifier.Array(rankSpecifier.Rank)); diff --git a/src/tests/SourceGeneratorFramework.Analyzers.UnitTests/TypeLibraryValidationAnalyzerTests.cs b/src/tests/SourceGeneratorFramework.Analyzers.UnitTests/TypeLibraryValidationAnalyzerTests.cs index e3bc69d..c76b6c0 100644 --- a/src/tests/SourceGeneratorFramework.Analyzers.UnitTests/TypeLibraryValidationAnalyzerTests.cs +++ b/src/tests/SourceGeneratorFramework.Analyzers.UnitTests/TypeLibraryValidationAnalyzerTests.cs @@ -40,7 +40,7 @@ public TypeRefAttribute(object type, string? @namespace = null, int arity = -1) public int Arity { get; set; } } - [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = true)] public sealed class EnumValueAttribute : Attribute { public EnumValueAttribute(string enumName, string @namespace, object value, string[]? aliases = null) @@ -63,6 +63,17 @@ public EnumValueAttribute(string enumFullName, object value, string[]? aliases = public object? Value { get; } public string[]? Aliases { get; } } + + [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] + public sealed class EnumValuesAttribute : Attribute + { + public EnumValuesAttribute(Type enumType) + { + EnumType = enumType; + } + + public Type? EnumType { get; } + } } namespace Purview.SourceGeneratorFramework @@ -768,4 +779,248 @@ static partial class TypeLibraryModel await Assert.That(result).HasNoDiagnostics(); } + + [Test] + public async Task Generate_ValidEnumValuesFromType_DoesNotReportDiagnostic(CancellationToken cancellationToken) + { + var source = + AttributeDefinition + + """ + namespace Test + { + public enum Status + { + Ready = 0, + Set = 1, + } + + [GenerateTypeLibrary] + static partial class TypeLibraryModel + { + [TypeRef(typeof(Status))] + static readonly TypeIdentity Status = default!; + + [EnumValues(typeof(Status))] + static readonly TypeIdentity StatusValues = default!; + } + } + """; + + var result = await AnalyzeAsync(source, cancellationToken); + + await Assert.That(result).HasNoDiagnostics(); + } + + [Test] + public async Task Generate_EnumValuesFromTypeWrongFieldType_ReportsEnumValueMemberTypeInvalid( + CancellationToken cancellationToken + ) + { + var source = + AttributeDefinition + + """ + namespace Test + { + public enum Status + { + Ready = 0, + } + + [GenerateTypeLibrary] + static partial class TypeLibraryModel + { + [TypeRef(typeof(Status))] + static readonly TypeIdentity Status = default!; + + [EnumValues(typeof(Status))] + static readonly string StatusValues = default!; + } + } + """; + + var result = await AnalyzeAsync(source, cancellationToken); + + await Assert.That(result).HasDiagnostics(1); + await Assert.That(result).HasDiagnostic(TypeLibraryValidationAnalyzer.EnumValueMemberTypeInvalid.Id); + } + + [Test] + public async Task Generate_EnumValuesFromTypeMissingEnumTypeRef_ReportsEnumTypeNotDeclared( + CancellationToken cancellationToken + ) + { + var source = + AttributeDefinition + + """ + namespace Test + { + public enum Status + { + Ready = 0, + } + + [GenerateTypeLibrary] + static partial class TypeLibraryModel + { + [EnumValues(typeof(Status))] + static readonly TypeIdentity StatusValues = default!; + } + } + """; + + var result = await AnalyzeAsync(source, cancellationToken); + + await Assert.That(result).HasDiagnostics(1); + await Assert.That(result).HasDiagnostic(TypeLibraryValidationAnalyzer.EnumValueEnumTypeNotDeclared.Id); + } + + [Test] + public async Task Generate_EnumValuesFromTypeNotAnEnum_ReportsTypeNotEnum(CancellationToken cancellationToken) + { + var source = + AttributeDefinition + + """ + namespace Test + { + public class NotAnEnum + { + } + + [GenerateTypeLibrary] + static partial class TypeLibraryModel + { + [EnumValues(typeof(NotAnEnum))] + static readonly TypeIdentity NotAnEnumValues = default!; + } + } + """; + + var result = await AnalyzeAsync(source, cancellationToken); + + await Assert.That(result).HasDiagnostics(1); + await Assert.That(result).HasDiagnostic(TypeLibraryValidationAnalyzer.EnumValuesTypeNotEnum.Id); + } + + [Test] + public async Task Generate_EnumValuesFromTypeOverlapsIndividual_ReportsDuplicateMember( + CancellationToken cancellationToken + ) + { + var source = + AttributeDefinition + + """ + namespace Test + { + public enum Status + { + Ready = 0, + Set = 1, + } + + [GenerateTypeLibrary] + static partial class TypeLibraryModel + { + [TypeRef(typeof(Status))] + static readonly TypeIdentity Status = default!; + + [EnumValue("Status", "Test", 0)] + static readonly TypeIdentity Ready = default!; + + [EnumValues(typeof(Status))] + static readonly TypeIdentity StatusValues = default!; + } + } + """; + + var result = await AnalyzeAsync(source, cancellationToken); + + await Assert.That(result).HasDiagnostic(TypeLibraryValidationAnalyzer.EnumValueDuplicateMember.Id); + } + + [Test] + public async Task Generate_EnumValuesFromTypeDuplicateBulkMarker_ReportsDuplicateMember( + CancellationToken cancellationToken + ) + { + var source = + AttributeDefinition + + """ + namespace Test + { + public enum Status + { + Ready = 0, + Set = 1, + } + + [GenerateTypeLibrary] + static partial class TypeLibraryModel + { + [TypeRef(typeof(Status))] + static readonly TypeIdentity Status = default!; + + [EnumValues(typeof(Status))] + static readonly TypeIdentity StatusValues = default!; + + [EnumValues(typeof(Status))] + static readonly TypeIdentity StatusValues2 = default!; + } + } + """; + + var result = await AnalyzeAsync(source, cancellationToken); + + await Assert.That(result).HasDiagnostic(TypeLibraryValidationAnalyzer.EnumValueDuplicateMember.Id); + } + + [Test] + public async Task Generate_ValidEnumValueOnTypeRefField_DoesNotReportDiagnostic(CancellationToken cancellationToken) + { + var source = + AttributeDefinition + + """ + namespace Test + { + [GenerateTypeLibrary] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + [EnumValue("Singleton", 0)] + [EnumValue("Scoped", 1)] + static readonly TypeIdentity ServiceLifetime = default!; + } + } + """; + + var result = await AnalyzeAsync(source, cancellationToken); + + await Assert.That(result).HasNoDiagnostics(); + } + + [Test] + public async Task Generate_EnumValueOnTypeRefFieldDuplicateMember_ReportsDuplicateMember( + CancellationToken cancellationToken + ) + { + var source = + AttributeDefinition + + """ + namespace Test + { + [GenerateTypeLibrary] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + [EnumValue("Singleton", 0)] + [EnumValue("Singleton", 1)] + static readonly TypeIdentity ServiceLifetime = default!; + } + } + """; + + var result = await AnalyzeAsync(source, cancellationToken); + + await Assert.That(result).HasDiagnostics(1); + await Assert.That(result).HasDiagnostic(TypeLibraryValidationAnalyzer.EnumValueDuplicateMember.Id); + } } diff --git a/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/AspireC4MarkerGenerator.cs b/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/AspireC4MarkerGenerator.cs index f0bd020..cbe8c06 100644 --- a/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/AspireC4MarkerGenerator.cs +++ b/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/AspireC4MarkerGenerator.cs @@ -14,7 +14,6 @@ public sealed class AspireC4MarkerGenerator : IIncrementalGenerator public void Initialize(IncrementalGeneratorInitializationContext context) { context.RegisterPostInitializationOutput(static spc => - { spc.AddSource( "AspireC4Marker.g.cs", SourceText.From( @@ -71,7 +70,7 @@ public SeverityAttribute(LikeC4Severity severity) """, System.Text.Encoding.UTF8 ) - ); - }); + ) + ); } } diff --git a/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/AttributeDataModelTypeLibraryTests.cs b/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/AttributeDataModelTypeLibraryTests.cs index edc5804..4948d6e 100644 --- a/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/AttributeDataModelTypeLibraryTests.cs +++ b/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/AttributeDataModelTypeLibraryTests.cs @@ -17,7 +17,7 @@ public AttributeDataModelTypeLibraryTestOptions() } /// -/// Options for testing the sample, +/// Options for testing the sample, /// referencing the real example types. /// public sealed record SeverityAttributeDataTestOptions : AttributeDataModelTypeLibraryTestOptions @@ -25,14 +25,14 @@ public sealed record SeverityAttributeDataTestOptions : AttributeDataModelTypeLi public SeverityAttributeDataTestOptions() { AdditionalAssemblyTypes = AdditionalAssemblyTypes.AddRange( - typeof(Purview.SourceGeneratorFramework.Examples.SeverityLevel), - typeof(Purview.SourceGeneratorFramework.Examples.SeverityAttribute) + typeof(Examples.SeverityLevel), + typeof(Examples.SeverityAttribute) ); } } /// -/// Covers the sample: the model's +/// Covers the sample: the model's /// [Generate] target references the generated type library's SeverityAttributeFullName constant and /// its IsEnum defaults are bare member names expanded against the real example attribute. /// diff --git a/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/SeverityMarkerGenerator.cs b/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/SeverityMarkerGenerator.cs index 161fe7e..89a4429 100644 --- a/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/SeverityMarkerGenerator.cs +++ b/src/tests/SourceGeneratorFramework.ExampleGenerator.UnitTests/SeverityMarkerGenerator.cs @@ -14,7 +14,6 @@ public sealed class SeverityMarkerGenerator : IIncrementalGenerator public void Initialize(IncrementalGeneratorInitializationContext context) { context.RegisterPostInitializationOutput(static spc => - { spc.AddSource( "SeverityMarker.g.cs", SourceText.From( @@ -44,7 +43,7 @@ public SeverityAttribute(LikeC4Severity severity) """, System.Text.Encoding.UTF8 ) - ); - }); + ) + ); } } diff --git a/src/tests/SourceGeneratorFramework.Generators.UnitTests/TypeLibraryGeneratorTests.cs b/src/tests/SourceGeneratorFramework.Generators.UnitTests/TypeLibraryGeneratorTests.cs index 88b44fc..8d08a71 100644 --- a/src/tests/SourceGeneratorFramework.Generators.UnitTests/TypeLibraryGeneratorTests.cs +++ b/src/tests/SourceGeneratorFramework.Generators.UnitTests/TypeLibraryGeneratorTests.cs @@ -858,6 +858,674 @@ static partial class TypeLibraryModel await Assert.That(generated).IsNull(); } + [Test] + public async Task Generate_EnumValuesFromType_EmitsValuesGroup(CancellationToken cancellationToken) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum ServiceLifetime + { + Singleton = 0, + Scoped = 1, + Transient = 2, + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + static readonly TypeIdentity ServiceLifetime = default; + + [EnumValues(typeof(ServiceLifetime))] + static readonly TypeIdentity ServiceLifetimeValues = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert.That(generated).Contains("public static partial class ServiceLifetimeValues"); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Singleton = new(ServiceLifetime, \"Singleton\", 0);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Scoped = new(ServiceLifetime, \"Scoped\", 1);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Transient = new(ServiceLifetime, \"Transient\", 2);" + ); + await Assert + .That(generated) + .Contains("public static global::Purview.SourceGeneratorFramework.EnumValueDefinition Get("); + await Assert.That(generated).Contains("if (Singleton.Matches(name))"); + await Assert + .That(generated) + .Contains("return global::Purview.SourceGeneratorFramework.EnumValueDefinition.Empty;"); + } + + [Test] + public async Task Generate_EnumValuesFromType_GenerateFullNameConst_EmitsPerValueConsts( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum ServiceLifetime + { + Singleton = 0, + Scoped = 1, + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test", GenerateFullNameConst = true)] + static readonly TypeIdentity ServiceLifetime = default; + + [EnumValues(typeof(ServiceLifetime))] + static readonly TypeIdentity ServiceLifetimeValues = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert + .That(generated) + .Contains("public const string ServiceLifetimeFullName = \"Test.ServiceLifetime\";"); + await Assert + .That(generated) + .Contains("public const string SingletonFullName = ServiceLifetimeFullName + \".\" + \"Singleton\";"); + await Assert + .That(generated) + .Contains("public const string ScopedFullName = ServiceLifetimeFullName + \".\" + \"Scoped\";"); + } + + [Test] + public async Task Generate_EnumValuesFromType_UnderlyingType_EmitsNamedArgument(CancellationToken cancellationToken) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum Status : byte + { + Ready = 5, + Busy = 6, + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("Status", "Test")] + static readonly TypeIdentity Status = default; + + [EnumValues(typeof(Status))] + static readonly TypeIdentity StatusValues = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Ready = new(Status, \"Ready\", 5, underlyingType: EnumUnderlyingType.Byte);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Busy = new(Status, \"Busy\", 6, underlyingType: EnumUnderlyingType.Byte);" + ); + } + + [Test] + public async Task Generate_EnumValuesFromType_UInt64Value_EmitsLiteralAndUnderlyingType( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum Big : ulong + { + Maximum = 18446744073709551615, + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("Big", "Test")] + static readonly TypeIdentity Big = default; + + [EnumValues(typeof(Big))] + static readonly TypeIdentity BigValues = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Maximum = new(Big, \"Maximum\", 18446744073709551615, underlyingType: EnumUnderlyingType.UInt64);" + ); + } + + [Test] + public async Task Generate_EnumValuesFromType_XmlDocumentation_EmitsEnumMemberDocs( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum ServiceLifetime + { + /// + /// A single instance is created and reused for the lifetime of the application. + /// + Singleton = 0, + + /// + /// A new instance is created once per scope. + /// + Scoped = 1, + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + static readonly TypeIdentity ServiceLifetime = default; + + [EnumValues(typeof(ServiceLifetime))] + static readonly TypeIdentity ServiceLifetimeValues = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert + .That(generated) + .Contains("A single instance is created and reused for the lifetime of the application."); + await Assert.That(generated).Contains("A new instance is created once per scope."); + } + + [Test] + public async Task Generate_EnumValuesFromType_TypeRefMarkers_ReferencesMarkerField( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum ServiceLifetime + { + Singleton = 0, + Scoped = 1, + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + static readonly TypeIdentity ServiceLifetime = default; + + [EnumValues(typeof(ServiceLifetime))] + static readonly TypeIdentity ServiceLifetimeValues = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var typeRefs = await GetGeneratedStringAsync( + result, + "TypeLibrary.SampleTypeLibrary.Test.TypeLibraryModel.TypeRefs.g.cs", + cancellationToken + ); + + await Assert.That(typeRefs).IsNotNull(); + await Assert.That(typeRefs).Contains("ServiceLifetimeValues"); + // Enum member names are not spec fields and must not be referenced by the generated partial. + await Assert.That(typeRefs).DoesNotContain("[Singleton]"); + await Assert.That(typeRefs).DoesNotContain("[Scoped]"); + } + + [Test] + public async Task Generate_EnumValuesFromType_MissingEnumTypeRef_DoesNotEmitGroup( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum ServiceLifetime + { + Singleton = 0, + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [EnumValues(typeof(ServiceLifetime))] + static readonly TypeIdentity ServiceLifetimeValues = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + // TLB0017 is blocking, so the type library is not generated at all. + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNull(); + } + + [Test] + public async Task Generate_EnumValuesFromType_NotAnEnum_DoesNotEmitGroup(CancellationToken cancellationToken) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [EnumValues(typeof(string))] + static readonly TypeIdentity NotEnum = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + // TLB0020 is blocking, so the type library is not generated at all. + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNull(); + } + + [Test] + public async Task Generate_EnumValuesFromType_CombinedWithEnumValue_DuplicateMember_DoesNotEmitGroup( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum ServiceLifetime + { + Singleton = 0, + Scoped = 1, + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + static readonly TypeIdentity ServiceLifetime = default; + + [EnumValue("ServiceLifetime", "Test", 0)] + static readonly TypeIdentity Singleton = default; + + [EnumValues(typeof(ServiceLifetime))] + static readonly TypeIdentity ServiceLifetimeValues = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + // TLB0018 (duplicate member) is blocking, so the type library is not generated at all. + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNull(); + } + + [Test] + public async Task Generate_EnumValuesFromType_CombinedWithEnumValue_NoOverlap_EmitsAll( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum ServiceLifetime + { + Singleton = 0, + Scoped = 1, + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + static readonly TypeIdentity ServiceLifetime = default; + + [EnumValues(typeof(ServiceLifetime))] + static readonly TypeIdentity ServiceLifetimeValues = default; + + [EnumValue("ServiceLifetime", "Test", 2)] + static readonly TypeIdentity Transient = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert.That(generated).Contains("public static partial class ServiceLifetimeValues"); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Singleton = new(ServiceLifetime, \"Singleton\", 0);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Scoped = new(ServiceLifetime, \"Scoped\", 1);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Transient = new(ServiceLifetime, \"Transient\", 2);" + ); + } + + [Test] + public async Task Generate_EnumValuesFromType_EmptyEnum_EmitsGroupWithGetOnly(CancellationToken cancellationToken) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + public enum EmptyEnum + { + } + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("EmptyEnum", "Test")] + static readonly TypeIdentity EmptyEnum = default; + + [EnumValues(typeof(EmptyEnum))] + static readonly TypeIdentity EmptyEnumValues = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert.That(generated).Contains("public static partial class EmptyEnumValues"); + await Assert + .That(generated) + .Contains("public static global::Purview.SourceGeneratorFramework.EnumValueDefinition Get("); + await Assert + .That(generated) + .Contains("return global::Purview.SourceGeneratorFramework.EnumValueDefinition.Empty;"); + await Assert.That(generated).DoesNotContain("EnumValueDefinition Singleton"); + } + + [Test] + public async Task Generate_EnumValueOnTypeRefField_EmitsValuesGroup(CancellationToken cancellationToken) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + [EnumValue("Singleton", 0)] + [EnumValue("Scoped", 1)] + [EnumValue("Transient", 2)] + static readonly TypeIdentity ServiceLifetime = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert.That(generated).Contains("public static partial class ServiceLifetimeValues"); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Singleton = new(ServiceLifetime, \"Singleton\", 0);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Scoped = new(ServiceLifetime, \"Scoped\", 1);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Transient = new(ServiceLifetime, \"Transient\", 2);" + ); + await Assert + .That(generated) + .Contains("public static global::Purview.SourceGeneratorFramework.EnumValueDefinition Get("); + await Assert.That(generated).Contains("if (Singleton.Matches(name))"); + } + + [Test] + public async Task Generate_EnumValueOnTypeRefField_GenerateFullNameConst_EmitsPerValueConsts( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test", GenerateFullNameConst = true)] + [EnumValue("Singleton", 0)] + [EnumValue("Scoped", 1)] + static readonly TypeIdentity ServiceLifetime = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert + .That(generated) + .Contains("public const string ServiceLifetimeFullName = \"Test.ServiceLifetime\";"); + await Assert + .That(generated) + .Contains("public const string SingletonFullName = ServiceLifetimeFullName + \".\" + \"Singleton\";"); + await Assert + .That(generated) + .Contains("public const string ScopedFullName = ServiceLifetimeFullName + \".\" + \"Scoped\";"); + } + + [Test] + public async Task Generate_EnumValueOnTypeRefField_UnderlyingType_EmitsNamedArgument( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("Status", "Test")] + [EnumValue("Ready", (byte)5)] + [EnumValue("Busy", (byte)6)] + static readonly TypeIdentity Status = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Ready = new(Status, \"Ready\", 5, underlyingType: EnumUnderlyingType.Byte);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Busy = new(Status, \"Busy\", 6, underlyingType: EnumUnderlyingType.Byte);" + ); + } + + [Test] + public async Task Generate_EnumValueOnTypeRefField_TypeRefMarkers_ReferencesFieldOnce( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + [EnumValue("Singleton", 0)] + [EnumValue("Scoped", 1)] + static readonly TypeIdentity ServiceLifetime = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var typeRefs = await GetGeneratedStringAsync( + result, + "TypeLibrary.SampleTypeLibrary.Test.TypeLibraryModel.TypeRefs.g.cs", + cancellationToken + ); + + await Assert.That(typeRefs).IsNotNull(); + await Assert.That(typeRefs).Contains("ServiceLifetime"); + // Enum member names are not spec fields and must not be referenced by the generated partial. + await Assert.That(typeRefs).DoesNotContain("[Singleton]"); + await Assert.That(typeRefs).DoesNotContain("[Scoped]"); + } + + [Test] + public async Task Generate_EnumValueOnTypeRefField_CombinedWithStandaloneMarker_EmitsAll( + CancellationToken cancellationToken + ) + { + var source = """ + using Purview.SourceGeneratorFramework; + using Purview.SourceGeneratorFramework.Generators; + + namespace Test; + + [GenerateTypeLibrary(ClassName = "SampleTypeLibrary", Namespace = "Test")] + static partial class TypeLibraryModel + { + [TypeRef("ServiceLifetime", "Test")] + [EnumValue("Singleton", 0)] + static readonly TypeIdentity ServiceLifetime = default; + + [EnumValue("ServiceLifetime", "Test", 1)] + static readonly TypeIdentity Scoped = default; + + [EnumValue("ServiceLifetime", "Test", 2)] + static readonly TypeIdentity Transient = default; + } + """; + + var result = await GenerateAsync(source, cancellationToken: cancellationToken); + + var generated = await GetGeneratedStringAsync(result, "Test.TypeLibraryModel.g.cs", cancellationToken); + + await Assert.That(generated).IsNotNull(); + await Assert.That(generated).Contains("public static partial class ServiceLifetimeValues"); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Singleton = new(ServiceLifetime, \"Singleton\", 0);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Scoped = new(ServiceLifetime, \"Scoped\", 1);" + ); + await Assert + .That(generated) + .Contains( + "public static readonly global::Purview.SourceGeneratorFramework.EnumValueDefinition Transient = new(ServiceLifetime, \"Transient\", 2);" + ); + } + static async Task GetGeneratedStringAsync( DriverRunResult result, string fileName, diff --git a/src/tests/SourceGeneratorShared.UnitTests/Extensions/Microsoft/CodeAnalysis/ISymbolExtensionsTests.cs b/src/tests/SourceGeneratorShared.UnitTests/Extensions/Microsoft/CodeAnalysis/ISymbolExtensionsTests.cs index 0988bbc..00fd252 100644 --- a/src/tests/SourceGeneratorShared.UnitTests/Extensions/Microsoft/CodeAnalysis/ISymbolExtensionsTests.cs +++ b/src/tests/SourceGeneratorShared.UnitTests/Extensions/Microsoft/CodeAnalysis/ISymbolExtensionsTests.cs @@ -89,7 +89,7 @@ public async Task HasNullDefaultValue_GivenConstNullLocal_ReturnsTrue(Cancellati var symbol = semanticModel.GetDeclaredSymbol( localDeclaration.Declaration.Variables[0], cancellationToken: cancellationToken - )!; + ); await Assert.That(symbol.HasNullDefaultValue()).IsTrue(); } @@ -109,7 +109,7 @@ public async Task HasNullDefaultValue_GivenConstNonNullLocal_ReturnsFalse(Cancel var symbol = semanticModel.GetDeclaredSymbol( localDeclaration.Declaration.Variables[0], cancellationToken: cancellationToken - )!; + ); await Assert.That(symbol.HasNullDefaultValue()).IsFalse(); }