Repository: AvaloniaUI/Avalonia
File: src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameIncrementalGenerator.cs (C#)

Code before:
        try
        {
            var document = XDocument.Parse(xaml);
            var classValue = document.Root.Attribute(XName.Get("Class", XamlNamespaces.Xaml2006))?.Value;
            if (classValue?.LastIndexOf('.') is { } lastDotIndex && lastDotIndex != -1)
            {
                return new(classValue.Substring(lastDotIndex + 1), classValue.Substring(0, lastDotIndex), EquatableList<ResolvedXmlName>.Empty);
            }
        }
        catch
        {
            // ignore
        }
        return null;
    }

    private static LinePositionSpan GetLinePositionSpan(XmlException ex)
    {
        var position = new LinePosition(Math.Max(0, ex.LineNumber - 1), Math.Max(0, ex.LinePosition - 1));
        return new(position, position);
    }

    internal record XmlClassInfo(
        string FilePath,
        ResolvedXmlView? XmlView,
        Diagnostic? Diagnostic);

    internal record ResolvedClassInfo(
        ResolvedView? View,
        bool CanAttachDevTools,
        EquatableList<Diagnostic> Diagnostics);
}


Review comment (performance):
`Diagnostic` shouldn't be part of the record. Even though it's equatable, two different diagnostics from two different compilations won't be because their `Location` is different, breaking caching. The [usual solution](https://andrewlock.net/creating-a-source-generator-part-9-avoiding-performance-pitfalls-in-incremental-generators/#6-take-care-with-diagnostics) is to have a `DiagnosticInfo` and `LocationInfo` records.

Code after:
        {
            var document = XDocument.Parse(xaml);
            var classValue = document.Root.Attribute(XName.Get("Class", XamlNamespaces.Xaml2006))?.Value;
            if (classValue?.LastIndexOf('.') is { } lastDotIndex && lastDotIndex != -1)
            {
                return new(classValue.Substring(lastDotIndex + 1), classValue.Substring(0, lastDotIndex), EquatableList<ResolvedXmlName>.Empty);
            }
        }
        catch
        {
            // ignore
        }
        return null;
    }

    private static LinePositionSpan GetLinePositionSpan(XmlException ex)
    {
        var position = new LinePosition(Math.Max(0, ex.LineNumber - 1), Math.Max(0, ex.LinePosition - 1));
        return new(position, position);
    }

    internal record XmlClassInfo(
        string FilePath,
        ResolvedXmlView? XmlView,
        DiagnosticFactory? Diagnostic);

    internal record ResolvedClassInfo(
        ResolvedView? View,
        bool CanAttachDevTools,
        EquatableList<DiagnosticFactory> Diagnostics);

    /// <summary>
    /// Avoid holding references to <see cref="Diagnostic"/> because it can hold references to <see cref="ISymbol"/>, <see cref="SyntaxTree"/>, etc.
    /// </summary>
    internal record DiagnosticFactory(DiagnosticDescriptor Descriptor, FileLinePositionSpan LinePosition, EquatableList<string> FormatArguments)
    {
        public Diagnostic Create() => Diagnostic.Create(Descriptor, 
            Location.Create(LinePosition.Path, default, new(LinePosition.StartLinePosition, LinePosition.EndLinePosition)),
            messageArgs: [.. FormatArguments]);
    }
}