返回

c#-查找语法树和引用符号上都有属性的方法?

发布时间:2022-09-07 22:36:37 309
# flask

我有一个引用类库的应用程序,在这两个类库中,有一些类型的某些方法具有我需要收集的特定属性。

我能够涵盖这两种情况,也就是说,在语法树(应用程序)和引用符号(类库)上找到这些具有特定属性的方法。

我唯一的问题是,我发现自己在搜索这些属性方面做了两倍的工作。

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace ClassLibrary1;

[Generator]
public sealed class HelloSourceGenerator : ISourceGenerator
    // https://github.com/JoanComasFdz/dotnet-how-to-debug-source-generator-vs2022
{
    public void Initialize(GeneratorInitializationContext context)
    {
    }

    public void Execute(GeneratorExecutionContext context)
    {
        const string assemblyFilter = "ClassLibrary"; // only match assemblies whose name starts with
        const string attributeName = "global::ClassLibrary2.TestAttribute"; // the attribute we're looking for in methods

        // gather symbols in referenced assemblies

        var stack1 = new Stack(); // scratch stack
        var stack2 = new Stack(); // output stack

        var symbols = context
            .Compilation
            .SourceModule
            .ReferencedAssemblySymbols
            .Where(s => s.Name.StartsWith(assemblyFilter)).OrderBy(s => s.Name);

        foreach (var assemblySymbol in symbols)
        {
            var assemblyMembers = assemblySymbol.GlobalNamespace.GetMembers();

            foreach (var symbol in assemblyMembers)
            {
                stack1.Push(symbol);
            }

            while (stack1.Any())
            {
                var pop = stack1.Pop();

                var members = pop.GetMembers();

                foreach (var item in members)
                {
                    if (item is INamespaceOrTypeSymbol namespaceOrTypeSymbol)
                    {
                        stack1.Push(namespaceOrTypeSymbol);
                    }
                }

                if (pop is ITypeSymbol typeSymbol)
                {
                    stack2.Push(typeSymbol);
                }
            }
        }

        // find methods who have the attribute we're looking for

        var dictionary = new Dictionary>(SymbolEqualityComparer.Default);

        // 1. in referenced assemblies

        foreach (var symbol in stack2)
        {
            var kind = symbol.TypeKind;
            if (kind is not (TypeKind.Class or TypeKind.Struct))
                continue;

            var members = symbol.GetMembers();

            foreach (var member in members)
            {
                if (member.Kind is not SymbolKind.Method)
                    continue;

                var attributes = member.GetAttributes();

                foreach (var attribute in attributes)
                {
                    var s = attribute.AttributeClass!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

                    if (!string.Equals(s, attributeName, StringComparison.Ordinal))
                        continue;

                    if (dictionary.ContainsKey(symbol))
                    {
                        dictionary[symbol].Add(member);
                    }
                    else
                    {
                        dictionary.Add(symbol, new List { member });
                    }
                }
            }
        }

        // 2. in syntax tree

        foreach (var tree in context.Compilation.SyntaxTrees)
        {
            var model = context.Compilation.GetSemanticModel(tree);

            foreach (var descendantNode in tree.GetRoot().DescendantNodes())
            {
                if (descendantNode is not ClassDeclarationSyntax && descendantNode is not StructDeclarationSyntax)
                    continue;

                var cs = model.GetDeclaredSymbol(descendantNode)!;

                foreach (var m in descendantNode.DescendantNodes().OfType())
                {
                    var ms = model.GetDeclaredSymbol(m)!;

                    foreach (var data in ms.GetAttributes())
                    {
                        var s = data.AttributeClass!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

                        if (!string.Equals(s, attributeName, StringComparison.Ordinal))
                            continue;

                        if (dictionary.ContainsKey(cs))
                        {
                            dictionary[cs].Add(ms);
                        }
                        else
                        {
                            dictionary.Add(cs, new List { ms });
                        }
                    }
                }
            }
        }

        // TODO generate some code

        var builder = new StringBuilder();

        builder.AppendLine(@"
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace PSX.Generated;

public static class Logging
{
    public static IReadOnlyDictionary Names { get; }

    static Logging()
    {
        var names = new Dictionary();
");
        ;
        foreach (var pair in dictionary)
        {
            var key = pair.Key.ToDisplayString();

            foreach (var symbol in pair.Value)
            {
                builder.AppendLine($@"
        names.Add(
            ""{key}"",
            ""{symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}""
        );");
            }
        }

        builder.AppendLine(@"
        Names = new ReadOnlyDictionary(names);
    }
}");

        var source = builder.ToString();

        context.AddSource("PSX.Generated.Logging.g.cs", source);
    }
}

输出:

这正是我所期望的,所有具有该属性的方法,无论它们是在应用程序中还是在其引用中,都会被找到,并从中生成一个helper类。

using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace PSX.Generated;

public static class Logging
{
    public static IReadOnlyDictionary Names { get; }

    static Logging()
    {
        var names = new Dictionary();


        names.Add(
            "ClassLibrary2.ImplementationStruct1",
            "Method1"
        );

        names.Add(
            "ClassLibrary2.ImplementationStruct1",
            "Method4"
        );

        names.Add(
            "ClassLibrary2.Inner.Implementation1",
            "Method1"
        );

        names.Add(
            "ClassLibrary2.Inner.Implementation1",
            "Method2"
        );

        names.Add(
            "ConsoleApp1.ImplementationClass2",
            "Method1"
        );

        names.Add(
            "ConsoleApp1.ImplementationClass2",
            "Method3"
        );

        names.Add(
            "ConsoleApp1.ImplementationStruct2",
            "Method1"
        );

        names.Add(
            "ConsoleApp1.ImplementationStruct2",
            "Method4"
        );

        Names = new ReadOnlyDictionary(names);
    }
}

问题:

有没有一种方法可以在语法树和引用符号上只搜索一次属性,而不是代码中的两次?

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像