SteveDunn / Vogen

A semi-opinionated library which is a source generator and a code analyser. It Source generates Value Objects
Apache License 2.0
877 stars 45 forks source link

Source generation should formally format the type it wraps #350

Open SteveDunn opened 1 year ago

SteveDunn commented 1 year ago

Describe the feature

This is a suggested improvement to the way the source is generated for the underlying type that is bing wrapped. There is no particular bug associated with this, but the suggested alternative looks a lot more robust in terms of including the namespace, any global alias if needed, and any escaping, if needed.

The current code, when setting the underlying type (VoWorkItem.cs), uses INamedTypeSymbol.FullName:

    public INamedTypeSymbol UnderlyingType
    {
        get => _underlyingType;
        set
        {
            _underlyingType = value;
            _underlyingTypeFullName = value.FullName() ?? value?.Name ?? throw new InvalidOperationException(
                "No underlying type specified - please file a bug at https://github.com/SteveDunn/Vogen/issues/new?assignees=&labels=bug&template=BUG_REPORT.yml");
        }

Instead of value.FullName, then falling back to value.Name, it should instead use

            _underlyingTypeFullName = value.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

That is defined in Roslyn as:

        /// <summary>
        /// Formats the names of all types and namespaces in a fully qualified style (including the global alias).
        /// </summary>
        /// <remarks>
        /// The current behavior will not output the fully qualified style as expected for member symbols (such as properties) because memberOptions is not set.
        /// For example, MyNamespace.MyClass.MyPublicProperty will return as MyPublicProperty.
        /// The current behavior displayed here will be maintained for backwards compatibility.
        /// </remarks>
        public static SymbolDisplayFormat FullyQualifiedFormat { get; } =
            new SymbolDisplayFormat(
                globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
                typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
                genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
                miscellaneousOptions:
                    SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
                    SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
SteveDunn commented 4 months ago

I found and fixed a bug in Roslyn that stops us from using this. Revisit in the future.