使用 Roslyn 语义模型在单个 .cs 文件中查找符号

2024-06-28

我正在使用 Roslyn 创建一个分析器,如果特定类以不同步的方式公开其字段,该分析器会向用户发出警告,以帮助防止竞争条件。

问题:

我目前有工作代码可以检查以确保字段是私有的。我在解决最后一个难题时遇到了麻烦:找出一种方法来确保所有字段只能在锁块内访问,因此它们(表面上)是同步的。

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FindSymbols;

namespace RaceConditions
{
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public class UnsynchronizedMemberAccess : DiagnosticAnalyzer
    {
        public const string DiagnosticId = "UnsynchronizedMemberAccess";
        internal static readonly LocalizableString Title = "UnsynchronizedMemberAccess Title";
        private static readonly LocalizableString MessageFormat = "Unsychronized fields are not thread-safe";
        private static readonly LocalizableString Description = "Accessing fields without a get/set methods synchronized with each other and the constructor may lead to race conditions";
        internal const string Category = "Race Conditions";

        private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }

        //meant to stop other classes and itself from accessing members in an unsychronized fashion.
        public override void Initialize(AnalysisContext analysisContext)
        {
            analysisContext.RegisterSemanticModelAction((context) =>
            {
                var model = context.SemanticModel;
                var root = model.SyntaxTree.GetRoot();
                var nodes = model.SyntaxTree.GetRoot().DescendantNodes();

                var fields = nodes.OfType<VariableDeclaratorSyntax>()
                        .Where(v => v.Ancestors().OfType<FieldDeclarationSyntax>().Any());
                //since (it appears) that you can't read/write to a an initialized field,
                //I think it means you can only read/write inside a block
                foreach (BlockSyntax b in nodes.OfType<BlockSyntax>())
                {
                    //where I plan to put code to check references to the fields
                }
            });
        }
    }
}

更具体地说,我希望能够确保引用荧光笔突出显示的所有内容(至少微软似乎是这么称呼它的)都位于锁定块内,而重载参数则不必如此。

using System;
using System.Linq;
using System.Activities;
using System.Activities.Statements;
using System.Data.SqlClient;

namespace Sandbox
{

    partial class Program
    {
        private int xe = 0, y = 0;

        public Program(int xe)
        {
            this.xe = xe;
        }

        void bleh()
        {
            if (xe == 0)
            {
                xe = xe + 1;
            }
        }

        static void Main(string[] args)
        {
            Program p0 = new Program(5),
                p1 = new Program(p0),
                p2 = new Program(p0.xe);

            Console.WriteLine(p1.xe);
            Console.Read();
        }
    }

    partial class Program
    {
        public Program(Program p) : this(p.xe) { }
    }
}

这个调查:

在这里,Josh Varty [1] 建议我使用SymbolFinder.FindReferencesAsync,这需要一个Solution目的。 Jason Malinowski [2] 说我不应该在分析器中使用 do this,因为制作MSBuildWorkspace得到一个Solution对象太慢,而这个人 [3] 为缓慢问题提供了不完整/缺失的解决方法(链接到ReferenceResolver好像坏了)。

我也研究过DataFlowAnalysis (SemanticModel.AnalyzeDataFlow()),但我在那里找不到任何特定的方法,显然可以让我保证我正在引用该字段xe,而不是局部变量xe.

问题:

我确实觉得我错过了一些非常明显的东西。有没有一些我忽略的优雅方法来实现这一点?如果答案使用语义模型,那就更好了,因为我希望我必须在其他分析器中使用它来找出数据/引用的来源,但我意识到存在局限性,因此任何没有语义模型的答案也是如此美好的。

Notes:

  • 显然,这个问题在Github上也遇到了[4],但显然它仍然在那里被跟踪,他们不知道分析器是否应该在项目级别进行分析。事情还没有解决。出于此分析器的目的,我将假设整个类包含在一个.cs文件。首先是小步骤,是吗?
  • 我还搜索了 John Koerner 的网站 [5] 和 Josh Varty 的网站 [6],但找不到与分析器和 DataFlowAnalysis 相关的任何内容。

诀窍是颠倒你提问的方式。从...来:

如何找到我想要确保同步的对该符号的所有引用?

但反而

在查看符号的使用时,如何确定它是否应该位于锁定语句内?

因为这提供了一个操作过程:您的分析器应该查看方法体中不在锁定语句中的每个标识符,调用SemanticModel.GetSymbolInfo(),获取正在引用的符号,然后检查该字段是否是通过您的逻辑(私有等)“同步”的字段。此时,由于您正在查看用途,因此可以标记该特定用途。

这种反转是我们期望分析器的编写方式,这并非偶然。原因主要是性能。想象一下您的分析器正在 Visual Studio 中运行,并且您删除了一行代码。如果分析器是按照“查看一个符号,现在询问所有用途”的模型编写的,则意味着所有执行此操作的分析器都可能必须从头开始重新运行。这对您的 CPU 或电池寿命不利。当问题像这样颠倒时,这意味着我们只需要重新分析该特定文件,因为您不会扩展到“给我一切”。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Roslyn 语义模型在单个 .cs 文件中查找符号 的相关文章

随机推荐