| File: Model\PropertyGenerationSpec.cs | Web Access |
| Project: src\runtime\src\libraries\System.Text.Json\gen\System.Text.Json.SourceGeneration.Roslyn4.4.csproj (System.Text.Json.SourceGeneration) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Text.Json.Serialization; using SourceGenerators; namespace System.Text.Json.SourceGeneration { /// <summary> /// Models a property for a generated type. /// </summary> /// <remarks> /// Type needs to be cacheable as a Roslyn incremental value so it must be /// /// 1) immutable and /// 2) implement structural (pointwise) equality comparison. /// /// We can get these properties for free provided that we /// /// a) define the type as an immutable C# record and /// b) ensure all nested members are also immutable and implement structural equality. /// /// When adding new members to the type, please ensure that these properties /// are satisfied otherwise we risk breaking incremental caching in the source generator! /// </remarks> [DebuggerDisplay("Name = {MemberName}, Type = {PropertyType.Name}")] public sealed record PropertyGenerationSpec { /// <summary> /// The exact name specified in the source code. This might be different /// from the <see cref="MemberName"/> because source code might be decorated /// with '@' for reserved keywords, e.g. public string @event { get; set; } /// </summary> public required string NameSpecifiedInSourceCode { get; init; } public required string MemberName { get; init; } /// <summary> /// Is this a property or a field? /// </summary> public required bool IsProperty { get; init; } /// <summary> /// If representing a property, returns true if either the getter or setter are public. /// </summary> public required bool IsPublic { get; init; } public required bool IsVirtual { get; init; } /// <summary> /// The property name specified via JsonPropertyNameAttribute, if available. /// </summary> public required string? JsonPropertyName { get; init; } /// <summary> /// The pre-determined JSON property name, accounting for <see cref="JsonNamingPolicy"/> /// specified ahead-of-time via <see cref="JsonSourceGenerationOptionsAttribute"/>. /// Only used in fast-path serialization logic. /// </summary> public required string EffectiveJsonPropertyName { get; init; } /// <summary> /// The field identifier used for storing JsonEncodedText for use by the fast-path serializer. /// </summary> public required string PropertyNameFieldName { get; init; } /// <summary> /// Whether the property has a set method. /// </summary> public required bool IsReadOnly { get; init; } /// <summary> /// Whether the property is marked `required`. /// </summary> public required bool IsRequired { get; init; } /// <summary> /// The property is marked with JsonRequiredAttribute. /// </summary> public required bool HasJsonRequiredAttribute { get; init; } /// <summary> /// Whether the property has an init-only set method. /// </summary> public required bool IsInitOnlySetter { get; init; } /// <summary> /// Whether the property has a public or internal (only usable when JsonIncludeAttribute is specified) /// getter that can be referenced in generated source code. /// </summary> public required bool CanUseGetter { get; init; } /// <summary> /// Whether the property has a public or internal (only usable when JsonIncludeAttribute is specified) /// setter that can be referenced in generated source code. /// </summary> public required bool CanUseSetter { get; init; } /// <summary> /// Whether the property getter returns a nullable type with a non-nullable annotation. /// </summary> public required bool IsGetterNonNullableAnnotation { get; init; } /// <summary> /// Whether the property setter accepts a nullable type with a non-nullable annotation. /// </summary> public required bool IsSetterNonNullableAnnotation { get; init; } /// <summary> /// The <see cref="JsonIgnoreCondition"/> for the property. /// </summary> public required JsonIgnoreCondition? DefaultIgnoreCondition { get; init; } /// <summary> /// The <see cref="JsonNumberHandling"/> for the property. /// </summary> public required JsonNumberHandling? NumberHandling { get; init; } /// <summary> /// The <see cref="JsonObjectCreationHandling"/> for the property. /// </summary> public required JsonObjectCreationHandling? ObjectCreationHandling { get; init; } /// <summary> /// The serialization order of the property. /// </summary> public required int Order { get; init; } /// <summary> /// Whether the property has the JsonIncludeAttribute. If so, non-public accessors can be used for (de)serialziation. /// </summary> public required bool HasJsonInclude { get; init; } /// <summary> /// Whether the property can use UnsafeAccessor for its getter/setter. /// This is true for non-generic types on .NET 8+ and for generic types on .NET 9+ /// (using a generic wrapper class). False when UnsafeAccessorAttribute is not available. /// </summary> public required bool CanUseUnsafeAccessors { get; init; } /// <summary> /// When <see cref="CanUseUnsafeAccessors"/> is true and the declaring type is generic, /// contains the FQN of the declaring type using open type parameters /// (e.g., "global::TestApp.MyGenericType<T>"). /// </summary> public string? OpenDeclaringTypeFQN { get; init; } /// <summary> /// When <see cref="CanUseUnsafeAccessors"/> is true and the declaring type is generic, /// contains the FQN of the property type using open type parameters /// (e.g., "T" for a generic property, "string" for a concrete one). /// </summary> public string? OpenPropertyTypeFQN { get; init; } /// <summary> /// The type parameter names of the generic declaring type (e.g., ["T"]). /// Null when the declaring type is not generic. /// </summary> public ImmutableEquatableArray<string>? DeclaringTypeParameterNames { get; init; } /// <summary> /// The combined type parameter constraint clauses of the generic declaring type /// (e.g., "where T : notnull, global::MyNamespace.MyBase where U : struct"). /// Null when the declaring type is not generic or has no constraints. /// </summary> public string? DeclaringTypeParameterConstraintClauses { get; init; } /// <summary> /// Whether the property has the JsonExtensionDataAttribute. /// </summary> public required bool IsExtensionData { get; init; } /// <summary> /// Gets a reference to the property type. /// </summary> public required TypeRef PropertyType { get; init; } /// <summary> /// Gets a reference to the declaring type of the property. /// </summary> public required TypeRef DeclaringType { get; init; } /// <summary> /// Design-time specified custom converter type. /// </summary> public required TypeRef? ConverterType { get; init; } /// <summary> /// Determines if the specified property should be included in the fast-path method body. /// </summary> public bool ShouldIncludePropertyForFastPath(ContextGenerationSpec contextSpec) { // Discard ignored properties if (DefaultIgnoreCondition is JsonIgnoreCondition.Always) { return false; } // Discard properties without getters, unless they have [JsonInclude] // in which case we use UnsafeAccessor or reflection to read the value. if (!CanUseGetter && !HasJsonInclude) { return false; } // Discard fields when JsonInclude or IncludeFields aren't enabled. if (!IsProperty && !HasJsonInclude && contextSpec.GeneratedOptionsSpec?.IncludeFields != true) { return false; } // Ignore read-only properties/fields if enabled in configuration. if (IsReadOnly) { if (IsProperty) { if (contextSpec.GeneratedOptionsSpec?.IgnoreReadOnlyProperties == true) { return false; } } else if (contextSpec.GeneratedOptionsSpec?.IgnoreReadOnlyFields == true) { return false; } } return true; } } }