Overview​
In the previous Attribute Parser article, we covered how IAttributeParser and BaseAttributeParser identify, validate, and extract metadata from bit-field attributes. This article focuses on the resulting data these parsers produce � specifically the IAttributeParsedResult, IFieldName, and their concrete implementations.
These data models standardize the parsed attribute information (e.g., how many bits were requested, which field name was referenced, or whether the field symbol exists). In more advanced scenarios (like read-only or �extended� attributes), they allow the parser to attach extra metadata (e.g., constructor visibility or large-size symbols).
Core Interfaces​
IAttributeParsedResult​
/// <summary>
/// Represents the result of parsing an attribute.
/// </summary>
/// <remarks>
/// This interface standardizes the properties that store data parsed from attributes,
/// including information about the attribute's syntax, associated data, and additional metadata.
/// </remarks>
internal interface IAttributeParsedResult
{
/// <summary>
/// Gets the <see cref="AttributeData"/> representing the parsed attribute metadata.
/// </summary>
public AttributeData AttributeData { get; }
/// <summary>
/// Gets the <see cref="AttributeSyntax"/> representing the syntax node of the parsed attribute.
/// </summary>
public AttributeSyntax AttributeSyntax { get; }
/// <summary>
/// Gets the number of bits specified by the parsed attribute, if applicable.
/// </summary>
/// <value>
/// The number of bits as a nullable <see cref="byte"/>, or <c>null</c> if not specified.
/// </value>
public byte? BitsCount { get; }
/// <summary>
/// Gets the field name specified by the parsed attribute, if applicable.
/// </summary>
/// <value>
/// The <see cref="IFieldName"/> representing the field name, or <c>null</c> if not specified.
/// </value>
public IFieldName? FieldName { get; }
}
Key Points:
- Combines parsed metadata (e.g.
BitsCount,FieldName) with Roslyn references (AttributeData,AttributeSyntax). - Enables polymorphic handling of different attribute variants (extended vs. read-only, etc.).
IFieldName​
internal interface IFieldName
{
/// <summary>
/// The name of the field.
/// Can be null if the field is unnamed.
/// </summary>
public string? Name { get; }
/// <summary>
/// Indicates whether the field is linked to an existing symbol.
/// If true, both <see cref="ExistingSymbol"/> and <see cref="Name"/> are guaranteed to be non-null.
/// </summary>
[MemberNotNullWhen(true, nameof(ExistingSymbol))]
[MemberNotNullWhen(true, nameof(Name))]
public bool IsSymbolExist { get; }
/// <summary>
/// The existing symbol associated with the field, if any.
/// Can be null if no symbol is associated.
/// </summary>
public IFieldSymbol? ExistingSymbol { get; }
}
Key Points:
- Field Name can be unnamed (if the attribute did not specify one).
- If
IsSymbolExist == true,ExistingSymbolreferences an actual C# field declared in the user�s code. - Used by the generator to determine if a property shares or references an existing bit-packed field.
Concrete Classes​
AttributeParsedResult (abstract)​
internal abstract class AttributeParsedResult(
AttributeSyntax attributeSyntax,
AttributeData attributeData,
IFieldName? fieldName,
byte? bitsCount
) : IAttributeParsedResult
{
public AttributeSyntax AttributeSyntax { get; } = attributeSyntax;
public AttributeData AttributeData { get; } = attributeData;
public IFieldName? FieldName { get; } = fieldName;
public byte? BitsCount { get; } = bitsCount;
}
Purpose: A base class capturing common parsed data (attribute syntax, bits count, optional field name). Concrete classes (like ParsedBitFiledAttribute) extend it with additional properties.
ParsedBitFiledAttribute​
internal sealed class ParsedBitFiledAttribute : AttributeParsedResult
{
public ParsedBitFiledAttribute(
AttributeSyntax attributeSyntax,
AttributeData attributeData,
IFieldName? fieldName,
byte? bitsCount
)
: base(attributeSyntax, attributeData, fieldName, bitsCount)
{
}
public override string ToString()
{
var nameOfFieldNameOrJustName = FieldName?.IsSymbolExist ?? false
? $"nameof({FieldName.Name})"
: FieldName?.Name ?? "<unnamed>";
return $"{nameof(BitFieldAttribute)}({nameof(BitsCount)}={BitsCount}, {nameof(FieldName)}={nameOfFieldNameOrJustName})";
}
}
Standard implementation for a typical bit-field attribute. Tracks:
BitsCount: Number of bits for the property.FieldName: The name (or symbol) for the bit field.
ParsedExtendedBitFiledAttribute & ParsedReadOnlyBitFieldAttribute​
These classes inherit AttributeParsedResult but add more specialized data:
ParsedExtendedBitFiledAttributeimplementsIParsedExtendedBitFiledAttributeand includes aSymbolGetterLargeSizeValueproperty for large bit-size handling.ParsedReadOnlyBitFieldAttributeimplementsIParsedReadOnlyBitFieldAttribute, storing aConstructorAccessModifierto control the accessibility of the generated constructor.
internal sealed class ParsedExtendedBitFiledAttribute
: AttributeParsedResult, IParsedExtendedBitFiledAttribute
{
public ISymbol SymbolGetterLargeSizeValue { get; }
public ParsedExtendedBitFiledAttribute(
AttributeSyntax attributeSyntax,
AttributeData attributeData,
IFieldName? fieldName,
byte? bitsCount,
ISymbol symbolGetterLargeSizeValue
) : base(attributeSyntax, attributeData, fieldName, bitsCount)
{
SymbolGetterLargeSizeValue = symbolGetterLargeSizeValue;
}
}
internal sealed class ParsedReadOnlyBitFieldAttribute
: AttributeParsedResult, IParsedReadOnlyBitFieldAttribute
{
public AccessModifier ConstructorAccessModifier { get; }
public ParsedReadOnlyBitFieldAttribute(
AttributeSyntax attributeSyntax,
AttributeData attributeData,
IFieldName? fieldName,
byte? bitsCount,
AccessModifier accessModifier
) : base(attributeSyntax, attributeData, fieldName, bitsCount)
{
ConstructorAccessModifier = accessModifier;
}
}
Mermaid Hierarchy Diagram​
Below is a Mermaid diagram illustrating how these types relate. Note that dashed arrows represent interface implementation:
classDiagram
class IAttributeParsedResult {
<<interface>>
+AttributeData : AttributeData
+AttributeSyntax : AttributeSyntax
+BitsCount : byte?
+FieldName : IFieldName?
}
class IParsedExtendedBitFiledAttribute {
<<interface>>
+SymbolGetterLargeSizeValue : ISymbol
}
class IParsedReadOnlyBitFieldAttribute {
<<interface>>
+ConstructorAccessModifier : AccessModifier
}
class IFieldName {
<<interface>>
+Name : string
+IsSymbolExist : bool
}
class AttributeParsedResult {
<<abstract>>
+AttributeData : AttributeData
+AttributeSyntax : AttributeSyntax
+BitsCount : byte?
+FieldName : IFieldName?
+AttributeParsedResult(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?)
}
class ParsedBitFiledAttribute {
+ParsedBitFiledAttribute(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?)
+ToString() : string
}
class ParsedExtendedBitFiledAttribute {
+ParsedExtendedBitFiledAttribute(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?, symbolGetterLargeSizeValue: ISymbol)
+SymbolGetterLargeSizeValue : ISymbol
+ToString() : string
}
class ParsedReadOnlyBitFieldAttribute {
+ParsedReadOnlyBitFieldAttribute(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?, accessModifier: AccessModifier)
+ConstructorAccessModifier : AccessModifier
+ToString() : string
}
class ParsedReadOnlyExtendedBitFieldAttribute {
+ParsedReadOnlyExtendedBitFieldAttribute(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?, accessModifier: AccessModifier, symbolGetterLargeSizeValue: ISymbol)
+ConstructorAccessModifier : AccessModifier
+SymbolGetterLargeSizeValue : ISymbol
+ToString() : string
}
class FieldName {
+Name : string
+IsSymbolExist : bool
+FieldName(name: string, isSymbolExist: bool)
}
IAttributeParsedResult <|.. AttributeParsedResult
AttributeParsedResult <|-- ParsedBitFiledAttribute
AttributeParsedResult <|-- ParsedExtendedBitFiledAttribute
AttributeParsedResult <|-- ParsedReadOnlyBitFieldAttribute
AttributeParsedResult <|-- ParsedReadOnlyExtendedBitFieldAttribute
IParsedExtendedBitFiledAttribute <|.. ParsedExtendedBitFiledAttribute
IParsedExtendedBitFiledAttribute <|.. ParsedReadOnlyExtendedBitFieldAttribute
IParsedReadOnlyBitFieldAttribute <|.. ParsedReadOnlyBitFieldAttribute
IParsedReadOnlyBitFieldAttribute <|.. ParsedReadOnlyExtendedBitFieldAttribute
IFieldName <|.. FieldName
classDiagram
class IAttributeParsedResult {
<<interface>>
+AttributeData : AttributeData
+AttributeSyntax : AttributeSyntax
+BitsCount : byte?
+FieldName : IFieldName?
}
class IParsedExtendedBitFiledAttribute {
<<interface>>
+SymbolGetterLargeSizeValue : ISymbol
}
class IParsedReadOnlyBitFieldAttribute {
<<interface>>
+ConstructorAccessModifier : AccessModifier
}
class IFieldName {
<<interface>>
+Name : string
+IsSymbolExist : bool
}
class AttributeParsedResult {
<<abstract>>
+AttributeData : AttributeData
+AttributeSyntax : AttributeSyntax
+BitsCount : byte?
+FieldName : IFieldName?
+AttributeParsedResult(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?)
}
class ParsedBitFiledAttribute {
+ParsedBitFiledAttribute(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?)
+ToString() : string
}
class ParsedExtendedBitFiledAttribute {
+ParsedExtendedBitFiledAttribute(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?, symbolGetterLargeSizeValue: ISymbol)
+SymbolGetterLargeSizeValue : ISymbol
+ToString() : string
}
class ParsedReadOnlyBitFieldAttribute {
+ParsedReadOnlyBitFieldAttribute(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?, accessModifier: AccessModifier)
+ConstructorAccessModifier : AccessModifier
+ToString() : string
}
class ParsedReadOnlyExtendedBitFieldAttribute {
+ParsedReadOnlyExtendedBitFieldAttribute(attributeSyntax: AttributeSyntax, attributeData: AttributeData, fieldName: IFieldName?, bitsCount: byte?, accessModifier: AccessModifier, symbolGetterLargeSizeValue: ISymbol)
+ConstructorAccessModifier : AccessModifier
+SymbolGetterLargeSizeValue : ISymbol
+ToString() : string
}
class FieldName {
+Name : string
+IsSymbolExist : bool
+FieldName(name: string, isSymbolExist: bool)
}
IAttributeParsedResult <|.. AttributeParsedResult
AttributeParsedResult <|-- ParsedBitFiledAttribute
AttributeParsedResult <|-- ParsedExtendedBitFiledAttribute
AttributeParsedResult <|-- ParsedReadOnlyBitFieldAttribute
AttributeParsedResult <|-- ParsedReadOnlyExtendedBitFieldAttribute
IParsedExtendedBitFiledAttribute <|.. ParsedExtendedBitFiledAttribute
IParsedExtendedBitFiledAttribute <|.. ParsedReadOnlyExtendedBitFieldAttribute
IParsedReadOnlyBitFieldAttribute <|.. ParsedReadOnlyBitFieldAttribute
IParsedReadOnlyBitFieldAttribute <|.. ParsedReadOnlyExtendedBitFieldAttribute
IFieldName <|.. FieldName
IAttributeParsedResultis the root interface.AttributeParsedResultis an abstract base class implementingIAttributeParsedResult.ParsedBitFiledAttribute,ParsedExtendedBitFiledAttribute,ParsedReadOnlyBitFieldAttribute, andParsedReadOnlyExtendedBitFieldAttributeall derive fromAttributeParsedResult.- Additional interfaces:
IParsedExtendedBitFiledAttributeandIParsedReadOnlyBitFieldAttributedefine extra properties for extended and read-only scenarios. IFieldNameis separate, implemented byFieldNameto represent an existing or unnamed field reference.
Summary​
IAttributeParsedResultis how the generator standardizes parsed attribute data � guaranteeing consistent fields (BitsCount,FieldName,AttributeData,AttributeSyntax).AttributeParsedResultis the concrete base, with specialized subclasses likeParsedBitFiledAttribute.- Optional extended or read-only attributes add further interfaces (
IParsedExtendedBitFiledAttribute,IParsedReadOnlyBitFieldAttribute) and derived classes to store extra data (like constructor visibility or large-size symbol references). IFieldName(and itsFieldNameimplementation) helps the generator handle property fields consistently, whether referencing an existing symbol or creating a new field on demand.
These types support the broader PropertyBitPack pipeline, ensuring that attribute parsing yields robust, type-safe data models that the BitFieldProperty Parser, Aggregators, and Syntax Generators can use to generate final source code.