来自 ReferenceLocation 的符号? [英] Symbol from ReferenceLocation?

查看:71
本文介绍了来自 ReferenceLocation 的符号?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Roslyn 编写一些代码来验证一些架构分层约束以帮助一些程序集整合.

I'm trying to write some code using Roslyn that verifies some architectural layering constraints to aid some assembly consolidation.

即:* 一个内部类型,必须在一个 .Internal 后缀的命名空间中.* 仅允许在 X.Y.Z 命名空间中使用X.Y.Z.Internal"类型.

i.e.: * An internal type, must be in an .Internal suffixed namespace. * A use of a 'X.Y.Z.Internal' type is only allowed from the X.Y.Z namespace.

解决这个问题的最简单的方法是根据 FAQ(9) 通过枚举找到所有内部类型,然后利用 SymbolFinder 找到所有引用并检查引用站点上的包含类型.如获取引用位置的符号所述,GetEnclosureSymbol() 并没有真正返回最有用的数据为此目的.

The simplest looking approach to solve this problem is to find all internal types via enumeration as per FAQ(9) and then utilize SymbolFinder to find all of the references and examine the containing type at the reference sites. As mentioned in Get Symbol for ReferenceLocation, GetEnclosingSymbol() doesn't really return the most useful data for this purpose.

看起来可能有一种利用 SymbolFinder.FindSymbolAtPosition 的有用方法,但遗憾的是,ISyntaxFactsService 和 ISemanticModelFactsService 似乎是内部的.

It looks like there might be a useful approach utilizing SymbolFinder.FindSymbolAtPosition, but sadly it appears that the ISyntaxFactsService and ISemanticModelFactsService are internal.

这是一种耻辱,因为这似乎是尝试 SemanticModelExtensions.GetSymbols() 内部扩展方法使用的方法的限制因素.

This is a shame, because that appears to be the limiting factor in trying the approach that the SemanticModelExtensions.GetSymbols() internal extension method uses.

我错过了一些简单的东西吗?我当然希望如此.

Am I missing something straightforward? I sure hope so.

推荐答案

TL;DR:我想我已经找到了一种有效的方法,尽管我不确定性能是否正常.

TL;DR: I think I've found a way that works, although I'm not sure if the performance will be ok or not.

以下解决方案涉及:

  • 从上面链接的 ReferenceLocatoinExtensions.cs 模式的 AddSymbolsAsync 方法内部调用 GetReferenceSymbolLocationsAsync.

  • calling GetReferenceSymbolLocationsAsync from inside the AddSymbolsAsync method from the ReferenceLocatoinExtensions.cs pattern that was linked to above.

使用FindNodeReferenceLocation转换成SyntaxNode

根据检索到的 SyntaxNode 相对于其父节点的位置,进行一些调整,直到我们找到正确的 SyntaxNode 将返回一个非空值来自 SemanticModel.GetDeclaredSymbol() 的值

Depending on where the retrieved SyntaxNode is with respect to its parent nodes, do some adjustment until we find the correct SyntaxNode that will return a non-null value from SemanticModel.GetDeclaredSymbol()

做一些额外的记录,以便能够在更高级别的代码中自定义规则.(额外的簿记类型在最后.)

Do some extra bookkeeping for purposes of being able to customize the rule at a higher level of the code. (The extra bookkeeping types are at the end.)

private static async Task<List<ReferencedSymbolLocation>> GetReferenceSymbolLocationsAsync(
    Solution solution,
    SemanticModel semanticModel,
    ReferenceLocation reference,
    CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();

    var syntaxTree = semanticModel.SyntaxTree;

    var position = reference.Location.SourceSpan.Start;
    var result = new List<ReferencedSymbolLocation>();

    if (position >= syntaxTree.Length)
    {
        return result;
    }

    var root = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
    var foundNode = root.FindNode(reference.Location.SourceSpan, false, getInnermostNodeForTie: false);

    // Try and keep track of what kind of syntactical context
    // we found the referenced symbol:
    ReferenceType discoveredType = ReferenceType.Other;

    for (var current = foundNode; current != null; current = current.Parent)
    {
        cancellationToken.ThrowIfCancellationRequested();

        if (current as BaseListSyntax != null)
        {
            discoveredType = ReferenceType.BaseClass;
            continue;
        }
        else if (current as ClassDeclarationSyntax != null)
        {
            if (discoveredType == ReferenceType.Other)
            {
                discoveredType = ReferenceType.Class;
            }
            result.Add(CreateReferenceSymbolLocation(reference, semanticModel, current, discoveredType, cancellationToken));
            break;
        }
        else if (current as PropertyDeclarationSyntax != null)
        {
            result.Add(CreateReferenceSymbolLocation(reference, semanticModel, current, ReferenceType.Property, cancellationToken));
            break;
        }
        else if (current as ParameterSyntax != null)
        {
            // This covers method parameters and lambda parameters:
            result.Add(CreateReferenceSymbolLocation(reference, semanticModel, current, ReferenceType.Parameter, cancellationToken));
            break;
        }
        else if (current?.Parent as VariableDeclarationSyntax != null)
        {
            var grandparent = current?.Parent?.Parent;
            var parent = current.Parent as VariableDeclarationSyntax;
            if (grandparent as LocalDeclarationStatementSyntax != null)
            {
                discoveredType = ReferenceType.LocalVariable;
            }
            // Ditto for field based things:
            else if (grandparent as BaseFieldDeclarationSyntax != null)
            {
                if (grandparent as FieldDeclarationSyntax != null)
                {
                    discoveredType = ReferenceType.Field;
                }
                else if (grandparent as EventFieldDeclarationSyntax != null)
                {
                    discoveredType = ReferenceType.Event;
                }
            }
            else if (grandparent as ForStatementSyntax != null)
            {
                discoveredType = ReferenceType.ForVariable;
            }
            else if (grandparent as FixedStatementSyntax != null)
            {
                discoveredType = ReferenceType.FixedVariable;
            }
            else if (grandparent as UsingStatementSyntax != null)
            {
                discoveredType = ReferenceType.UsingVariable;
            }
            foreach (var variable in parent.Variables)
            {
                result.Add(CreateReferenceSymbolLocation(reference, semanticModel, variable, discoveredType, cancellationToken));
            }
            break;
        }
        else if (current as InvocationExpressionSyntax != null)
        {
            discoveredType = ReferenceType.MethodInvocation;
            continue;
        }
        else if (current as ObjectCreationExpressionSyntax != null)
        {
            // This covers constructing a class directly 'new XYZ()'
            // and 'new Action<XYZ>()':
            discoveredType = ReferenceType.ObjectCreation;
            continue;
        }
        else if (current as MethodDeclarationSyntax != null)
        {
            result.Add(CreateReferenceSymbolLocation(reference, semanticModel, current, discoveredType, cancellationToken));
            break;
        }
    }
    return result;
}

private static ReferencedSymbolLocation CreateReferenceSymbolLocation(
    ReferenceLocation reference,
    SemanticModel semanticModel,
    SyntaxNode node,
    ReferenceType referenceType,
    CancellationToken cancellationToken)
{
    return new ReferencedSymbolLocation(reference, semanticModel.GetDeclaredSymbol(node, cancellationToken), referenceType);
}

public enum ReferenceType
{
    None = 0,
    /// <summary>
    /// Used for ReferenceSymbolLocations where the context of the reference
    /// isn't yet in this enumeration. ReferenceSymbolLocation.ReferencedSymbol will point at the
    /// declaration that contains the ReferenceLocation.
    /// </summary>
    Other,
    Class,
    BaseClass,
    Field,
    Property,
    Parameter,
    Event,
    LocalVariable,
    ForVariable,
    FixedVariable,
    UsingVariable,
    // The following are related to references found inside of statements:
    MethodInvocation,
    ObjectCreation,
}

public class ReferencedSymbolLocation
{
    public ReferenceLocation ReferenceLocation { get; private set; }
    public ISymbol ReferencedSymbol { get; private set; }
    public ReferenceType ReferenceType { get; private set; }

    internal ReferencedSymbolLocation(ReferenceLocation location, ISymbol referencedSymbol, ReferenceType referenceType)
    {
        ReferenceLocation = location;
        ReferencedSymbol = referencedSymbol;
        ReferenceType = referenceType;
    }
}

这篇关于来自 ReferenceLocation 的符号?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆