namespace G { [Generator(LanguageNames.CSharp)] public class OperatorsGenerator : IIncrementalGenerator { private const string AttributeSource = @"// using System; namespace QWERTYkez.Mensura { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] internal sealed class OperatorsGeneratorAttribute : Attribute { public string CoeffName { get; } public OperatorsGeneratorAttribute() { } public OperatorsGeneratorAttribute(string coeffName) { CoeffName = coeffName; } } }"; public void Initialize(IncrementalGeneratorInitializationContext context) { // 1. Внедряем атрибут context.RegisterPostInitializationOutput(static postInitializationContext => { postInitializationContext.AddSource( ".OperatorsGeneratorAttribute.g.cs", SourceText.From(AttributeSource, Encoding.UTF8)); }); // 2. Ищем методы-операторы, помеченные атрибутом IncrementalValuesProvider operatorDeclarations = context.SyntaxProvider .ForAttributeWithMetadataName( fullyQualifiedMetadataName: "QWERTYkez.Mensura.OperatorsGeneratorAttribute", predicate: static (node, _) => node is OperatorDeclarationSyntax, transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)) .Where(static m => m is not null)!; // 3. Выполняем генерацию кода context.RegisterSourceOutput(operatorDeclarations, static (spc, opInfo) => Execute(spc, opInfo)); } private static OperatorInfo GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context) { var opDeclaration = (OperatorDeclarationSyntax)context.TargetNode; if (opDeclaration.ParameterList.Parameters.Count < 2) return null; if (context.SemanticModel.GetDeclaredSymbol(opDeclaration) is not IMethodSymbol symbol) return null; var namespaceName = symbol.ContainingNamespace.ToDisplayString(); var leftType = opDeclaration.ParameterList.Parameters[0].Type?.ToString() ?? ""; var rightType = opDeclaration.ParameterList.Parameters[1].Type?.ToString() ?? ""; var returnType = opDeclaration.ReturnType.ToString(); var isMultiplication = opDeclaration.OperatorToken.IsKind(SyntaxKind.AsteriskToken); var isDivision = opDeclaration.OperatorToken.IsKind(SyntaxKind.SlashToken); if (!isMultiplication && !isDivision) return null; // АВТОМАТИЧЕСКИЙ ПОИСК: Ищем содержащий тип (структуру/рекорд) по синтаксическому дереву string containingTypeName = leftType; // Фолбэк по умолчанию var parentTypeDeclaration = opDeclaration.Ancestors() .OfType() .FirstOrDefault(); if (parentTypeDeclaration != null) { containingTypeName = parentTypeDeclaration.Identifier.ToString(); } string coeffName = null; string coeffType = containingTypeName; // По умолчанию считаем, что коэффициент в текущем классе // Извлекаем аргумент атрибута var attribute = opDeclaration.AttributeLists .SelectMany(al => al.Attributes) .FirstOrDefault(a => a.Name.ToString().EndsWith("OperatorsGenerator") || a.Name.ToString() == "OperatorsGenerator"); if (attribute?.ArgumentList != null && attribute.ArgumentList.Arguments.Count > 0) { var expr = attribute.ArgumentList.Arguments[0].Expression; // Проверяем синтаксис nameof(...) if (expr is InvocationExpressionSyntax invocation && invocation.Expression.ToString() == "nameof" && invocation.ArgumentList.Arguments.Count > 0) { var nameofArg = invocation.ArgumentList.Arguments[0].Expression; // Если все-таки написали явно nameof(Type.Property) if (nameofArg is MemberAccessExpressionSyntax memberAccess) { coeffType = memberAccess.Expression.ToString(); coeffName = memberAccess.Name.ToString(); } else { coeffName = nameofArg.ToString(); // coeffType остается равным содержащему классу containingTypeName } } else { coeffName = expr.ToString().Trim('"'); // coeffType остается равным содержащему классу containingTypeName } } return new OperatorInfo { Namespace = namespaceName, LeftType = leftType, RightType = rightType, ReturnType = returnType, IsMultiplication = isMultiplication, IsDivision = isDivision, CoeffName = coeffName, CoeffType = coeffType, HasCoeff = !string.IsNullOrWhiteSpace(coeffName) }; } private static void Execute(SourceProductionContext context, OperatorInfo info) { var opType = info.IsMultiplication ? "Mul" : "Div"; var coeffSuffix = info.HasCoeff ? ".Coeff" : ""; var hintName = $"Op.{info.LeftType}.{opType}.{info.RightType}{coeffSuffix}.g.cs"; var source = info.IsMultiplication ? GenerateMultiplicationSource(info) : GenerateDivisionSource(info); context.AddSource(hintName, SourceText.From(source, Encoding.UTF8)); } private static string GenerateMultiplicationSource(OperatorInfo info) { return $@"// using System; using System.Collections.Generic; namespace {info.Namespace}; public readonly partial record struct {info.LeftType} {{ public static {info.ReturnType}[] operator *({info.LeftType} left, {info.RightType}[] right) => {(info.HasCoeff ? $"({info.CoeffType}.{info.CoeffName} * left._Value)" : "left._Value")}.Mul<{info.RightType}, {info.ReturnType}>(right); public static List<{info.ReturnType}> operator *({info.LeftType} left, List<{info.RightType}> right) => {(info.HasCoeff ? $"({info.CoeffType}.{info.CoeffName} * left._Value)" : "left._Value")}.Mul<{info.RightType}, {info.ReturnType}>(right); public static IEnumerable<{info.ReturnType}> operator *({info.LeftType} left, IEnumerable<{info.RightType}> right) => {(info.HasCoeff ? $"({info.CoeffType}.{info.CoeffName} * left._Value)" : "left._Value")}.Mul<{info.RightType}, {info.ReturnType}>(right); }} public readonly partial record struct {info.RightType} {{ public static {info.ReturnType}[] operator *({info.LeftType}[] left, {info.RightType} right) => {(info.HasCoeff ? $"({info.CoeffType}.{info.CoeffName} * right._Value)" : "right._Value")}.Mul<{info.LeftType}, {info.ReturnType}>(left); public static List<{info.ReturnType}> operator *(List<{info.LeftType}> left, {info.RightType} right) => {(info.HasCoeff ? $"({info.CoeffType}.{info.CoeffName} * right._Value)" : "right._Value")}.Mul<{info.LeftType}, {info.ReturnType}>(left); public static IEnumerable<{info.ReturnType}> operator *(IEnumerable<{info.LeftType}> left, {info.RightType} right) => {(info.HasCoeff ? $"({info.CoeffType}.{info.CoeffName} * right._Value)" : "right._Value")}.Mul<{info.LeftType}, {info.ReturnType}>(left); }}"; } private static string GenerateDivisionSource(OperatorInfo info) { return $@"// using System; using System.Collections.Generic; namespace {info.Namespace}; public readonly partial record struct {info.LeftType} {{ public static {info.ReturnType}[] operator /({info.LeftType} left, {info.RightType}[] right) => {(info.HasCoeff ? $"({info.CoeffType}.{info.CoeffName} * left._Value)" : "left._Value")}.Div<{info.RightType}, {info.ReturnType}>(right); public static List<{info.ReturnType}> operator /({info.LeftType} left, List<{info.RightType}> right) => {(info.HasCoeff ? $"({info.CoeffType}.{info.CoeffName} * left._Value)" : "left._Value")}.Div<{info.RightType}, {info.ReturnType}>(right); public static IEnumerable<{info.ReturnType}> operator /({info.LeftType} left, IEnumerable<{info.RightType}> right) => {(info.HasCoeff ? $"({info.CoeffType}.{info.CoeffName} * left._Value)" : "left._Value")}.Div<{info.RightType}, {info.ReturnType}>(right); }} public readonly partial record struct {info.RightType} {{ public static {info.ReturnType}[] operator /({info.LeftType}[] left, {info.RightType} right) => {(info.HasCoeff ? $"left.Mul<{info.LeftType}, {info.ReturnType}>({info.CoeffType}.{info.CoeffName} / right._Value)" : $"left.Div<{info.LeftType}, {info.ReturnType}>(right._Value)")}; public static List<{info.ReturnType}> operator /(List<{info.LeftType}> left, {info.RightType} right) => {(info.HasCoeff ? $"left.Mul<{info.LeftType}, {info.ReturnType}>({info.CoeffType}.{info.CoeffName} / right._Value)" : $"left.Div<{info.LeftType}, {info.ReturnType}>(right._Value)")}; public static IEnumerable<{info.ReturnType}> operator /(IEnumerable<{info.LeftType}> left, {info.RightType} right) => {(info.HasCoeff ? $"left.Mul<{info.LeftType}, {info.ReturnType}>({info.CoeffType}.{info.CoeffName} / right._Value)" : $"left.Div<{info.LeftType}, {info.ReturnType}>(right._Value)")}; }}"; } private class OperatorInfo { public string Namespace { get; set; } = string.Empty; public string LeftType { get; set; } = string.Empty; public string RightType { get; set; } = string.Empty; public string ReturnType { get; set; } = string.Empty; public bool IsMultiplication { get; set; } public bool IsDivision { get; set; } public string CoeffName { get; set; } public string CoeffType { get; set; } public bool HasCoeff { get; set; } } } }