Генератор исходного кода не соответствует атрибуту, объявленному в классе с фактическим определением атрибутаC#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Генератор исходного кода не соответствует атрибуту, объявленному в классе с фактическим определением атрибута

Сообщение Anonymous »

Использовать в случае < /p>
Учите класс с определенным атрибутом, во время компиляции ищите классы с этим атрибутом, оцените все свойства для класса, добавьте [Display (name = «Некоторое значение»)] к свойствам, если у них еще нет декоратора [Display]. < /p>
Attrubty Dectbure, Dectride, Dectured, Dectorator, Dectorator Generator. class = "lang-cs prettyprint-override">[AttributeUsage(AttributeTargets.Class)]
public class AutoDisplayAttribute : Attribute
{
public AutoDisplayMode Mode { get; }
public AutoDisplayAttribute(AutoDisplayMode mode = AutoDisplayMode.All) { Mode = mode; }
}
< /code>
Пример использования: < /p>
[AutoDisplay]
public class FancyClass
{
public string ProjectName { get; set; }
}

< /code>
Generator: < /p>
[Generator]
public class DisplayAttributeGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (node, _) =>
node is ClassDeclarationSyntax cds &&
cds.AttributeLists.Count > 0,
transform: static (ctx, _) => ctx.SemanticModel.GetDeclaredSymbol((ClassDeclarationSyntax)ctx.Node) as INamedTypeSymbol)
.Where(static symbol => symbol is not null);

var autoDisplayAttrSymbol = context.CompilationProvider
.Select((compilation, _) =>
compilation.GetTypeByMetadataName(typeof(AutoDisplayAttribute).FullName));

var filteredClasses = classDeclarations
.Combine(autoDisplayAttrSymbol)
.Select((tuple, _) =>
{
var (classSymbol, attrSymbol) = tuple;

if (classSymbol is null) return default;

foreach (var value in classSymbol.GetAttributes())
{
var aSymbol = value.AttributeClass;
Debug.WriteLine($"Attr: {aSymbol?.ToDisplayString()} | Assembly: {aSymbol?.ContainingAssembly.Name}");
Debug.WriteLine($"Target: {attrSymbol?.ToDisplayString()} | Assembly: {attrSymbol?.ContainingAssembly.Name}");
Debug.WriteLine($"Equality: {SymbolEqualityComparer.Default.Equals(aSymbol, attrSymbol)}");
}

var attr = classSymbol.GetAttributes()
.FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol));

if (attr is null) return default;

var mode = AutoDisplayMode.All;

if (attr.ConstructorArguments.Length > 0)
{
var arg = attr.ConstructorArguments[0];
if (arg.Kind == TypedConstantKind.Enum && arg.Type?.Name == nameof(AutoDisplayMode))
mode = (AutoDisplayMode)arg.Value!;
}

return (classSymbol, mode);
})
.Where(static result => result != default);

var classWithProperties = filteredClasses
.Select((tuple, _) =>
{
var (classSymbol, mode) = tuple;
var props = classSymbol.GetMembers()
.OfType()
.Where(p =>
!HasDisplay(p) &&
(mode != AutoDisplayMode.ValidationOnly || HasValidation(p)))
.ToList();

return (classSymbol, props);
})
.Where(pair => pair.props.Count > 0);

context.RegisterSourceOutput(classWithProperties, (spc, data) =>
{
var (classSymbol, props) = data;
var generatedCode = GeneratePartialClass(classSymbol, props);
spc.AddSource($"{classSymbol.Name}.DisplayAttributes.g.cs", generatedCode);
});
}

private bool HasDisplay(IPropertySymbol prop) =>
prop.GetAttributes().Any(a =>
a.AttributeClass?.Name == "DisplayAttribute");

private bool HasValidation(IPropertySymbol prop) =>
prop.GetAttributes().Any(a =>
a.AttributeClass?.BaseType?.Name == "ValidationAttribute");

private string GeneratePartialClass(INamedTypeSymbol classSymbol, List props)
{
var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();

var lines = props.Select(p =>
$" [Display(Name = \"some value\")]\n public {p.Type} {p.Name} {{ get; set; }}");

return $$"""
//
using System.ComponentModel.DataAnnotations;
namespace {{namespaceName}}
{
public partial class {{classSymbol.Name}}
{
{{string.Join("\n\n", lines)}}
}
}
""";
}
}
< /code>
Многое из этого было сгенерировано с помощью ИИ. У меня есть достойное понимание того, что здесь происходит, но я хотел, чтобы набор тестов проверил, что он сработал. Настройка тестового проекта, и во время теста стало необходимо понять, где генерируются файлы. Это привело к созданию процесса, чтобы вызвать генератор и посмотреть, где закончился выход.[Fact]
public void PrintGeneratedSources()
{
var source =
"""
using System;
namespace FanceNamespace
{
[AutoDisplay]
public class FancyClass
{
public string DisplayName { get; set; }
}
}
""";

var test = new CSharpSourceGeneratorTest
{
TestState =
{
Sources = { _usings, source },
AdditionalReferences = { MetadataReference.CreateFromFile(typeof(AutoDisplayAttribute).Assembly.Location) }
}
};

var results = SourceGenInspector.GenerateCompilationOutput(new DisplayAttributeGenerator(), test.TestState);
foreach (var (fileName, content) in results)
{
_output.WriteLine($"Generated file: {fileName}\n{content}");
}
}
< /code>
sourcegeninspector.generateCompilationOutput:
public static List GenerateCompilationOutput(T generator, SolutionState state) where T : IIncrementalGenerator
{
var syntaxTrees = state.Sources
.Select(source => CSharpSyntaxTree.ParseText(source.content))
.ToList();

var references = state.AdditionalReferences.Any()
? state.AdditionalReferences
: new List { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) };

var compilation = CSharpCompilation.Create("SourceGenDump")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddSyntaxTrees(syntaxTrees)
.AddReferences(references);

GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
driver = driver.RunGenerators(compilation);

var runResults = driver.GetRunResult();

return runResults
.GeneratedTrees
.Select(tree =>
(
tree.FilePath ?? "",
tree.GetText().ToString()
)).ToList();
}
< /code>
Наконец, проблема. Я не был сгенерирован, напечатанный для моего теста. Я отслеживал его до того, что генератор не был правильно соответствует декоратору [AutoDisplay] на FancyClass с определением того, что такое AutoDisplayAttribute. Я добавил заявления Debug.writeline, чтобы получить это понимание. Вывод для этих операторов Debug.WriteLine - < /p>
Attr: AutoDisplay | Assembly: SourceGenDump
Target: AutoDisplayGenerator.AutoDisplayAttribute | Assembly: AutoDisplayGenerator
Equality: False
< /code>
Я знаю, что не знаю достаточно о Рослине, чтобы понять, почему я не могу установить связь здесь. Чего не хватает моего кода, чтобы добраться?


Подробнее здесь: https://stackoverflow.com/questions/797 ... st-the-act
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»