来自ReferenceLocation的符号?

时间:2016-03-10 14:14:53

标签: c# roslyn

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

即: *内部类型,必须位于.Internal后缀命名空间中。 *只允许从X.Y.Z命名空间使用'X.Y.Z.Internal'类型。

解决此问题的最简单方法是根据FAQ(9)通过枚举查找所有内部类型,然后使用SymbolFinder查找所有引用并检查引用站点的包含类型。正如Get Symbol for ReferenceLocation中提到的,GetEnclosingSymbol()并没有真正为此目的返回最有用的数据。

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

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

我错过了一些直截了当的话吗?我当然希望如此。

2 个答案:

答案 0 :(得分:0)

SymbolFinder需要Workspace,在MsBuild中运行时不需要SymbolFinder。所以基本上如果你想使用NamedTypes,你必须在Visual Studio中,而不是让我们说CI工作。

内部类型,必须位于.Internal后缀命名空间中。 通过在INamedSymbol上注册符号操作可以轻松解决这个问题。在你的回调中,你将获得一个internal,你必须检查它是否是ContainingType,以及它是否在一个正确命名的命名空间内。 (注意,您可能需要考虑使用嵌套类型执行的操作。)

只允许从X.Y.Z命名空间使用'X.Y.Z.Internal'类型。这更难处理。如果只在VS内部工作是一个选项,那么您可以使用FindReferencesAsync找到上述每个类型符号的所有引用。您所指的链接有很好的建议。您可以将封闭符号视为已完成here。然后你需要找到方法/属性/字段是否在适当命名的命名空间中。因此,您必须在ContainingNamespaceSyntaxNode上向上走符号声明树。

我猜你想把这个分析作为CI工作的一部分来运行,所以这个解决方案不适合你的需要。在这种情况下,您可以为所有标识符注册ISymbol操作,获取基础class my_class(models.Model): _name = "my.model" name = fields.Char('Name', required=True) issue_type_id = fields.Many2one('my.model.type',"My Model Type", domain='_search_my_model_types', required=True) @api.model def _search_my_model_types(self): my_model_type_ids = [] return [('id', 'in', my_model_type_ids)] class my_class_type(models.Model): _name = "my.model.type" name = fields.Char("Name") 和封闭符号。然后你和以前一样。所以你可以查看名字。 (此解决方案可能很慢,您必须检查性能是否适合您的需求。)

答案 1 :(得分:0)

TL; DR:我想我找到了一种有效的方法,虽然我不确定表现是否合适。

以下解决方案包括:

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

  • 使用FindNodeReferenceLocation转换为SyntaxNode

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

  • 为了能够在更高级别的代码中自定义规则,请执行一些额外的簿记。 (额外的簿记类型在最后。)

    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;
        }
    }