用调用替换C#代码中的未申报变量

发布于 2025-02-12 18:45:42 字数 9744 浏览 0 评论 0原文

延续(用方法)

我需要用索引器表达式将C#代码中的所有未宣布的变量替换。例如,代码:

var b = 28;
return a + b;

需要将其转换为:

var b = 28;
return _resolver["a"] + b;

_Resolver对象的字符串索引器返回dynamic。当然,这可能仍然在运行时失败,但是我想达到至少编译的地步。

我已经在上面的示例中为“隔离”变量工作,但是当调用未宣布的变量上的方法或属性时,它会失败。例如:

return a.FavouriteColour;

csharpsyntaxrewriter类中抛出异常。我可能会做一个我不应该做的语法节点替代,但是我看不出应该如何做不同的做法。

我的重写器代码:

using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace RoslynConfusion
{
  public static class UndeclaredReplacement
  {
    public static string ReplaceUndeclared(string code)
    {
      var tree = CSharpSyntaxTree.ParseText(code);
      var compilation = CSharpCompilation.Create("Test", new[] { tree }, References);
      var rewriter = new UndeclaredRewriter(compilation.GetSemanticModel(tree));

      var root = tree.GetRoot();
      root = rewriter.Visit(root);

      return root.SyntaxTree.ToString();
    }

    private static IEnumerable<MetadataReference> References
    {
      get
      {
        yield return MetadataReference.CreateFromFile(typeof(int).Assembly.Location);
      }
    }
    private sealed class UndeclaredRewriter : CSharpSyntaxRewriter
    {
      private readonly SemanticModel _model;
      public UndeclaredRewriter(SemanticModel model)
      {
        _model = model ?? throw new ArgumentNullException(nameof(model));
      }

      public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
      {
        var nodeʹ = base.VisitIdentifierName(node);
        var symbol = _model.GetSymbolInfo(node);

        // We only care about *undeclared* identifiers...
        if (symbol.Symbol != null) return nodeʹ;
        if (symbol.CandidateSymbols.Any()) return nodeʹ;

        // ... and we don't care about unknown method invokes either.
        var parent = nodeʹ.Parent;
        if (parent.IsKind(SyntaxKind.InvocationExpression))
          return nodeʹ;

        return CreateResolverSyntax(node.Identifier);
      }
      private SyntaxNode CreateResolverSyntax(SyntaxToken token)
      {
        var literal = LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(token.ToString()));
        var indexSyntax = BracketedArgumentList(SingletonSeparatedList(Argument(literal)));
        var resolveSyntax = ElementAccessExpression(IdentifierName("_resolver"), indexSyntax);
        return resolveSyntax;
      }
    }
  }
}

以及带有两个失败情况的测试方法:

    [TestMethod]
    public void TestUndeclaredReplacement()
    {
      var replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return a;");
      Assert.AreEqual(replaced, "return _resolver[\"a\"];");

      replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return a + b;");
      Assert.AreEqual(replaced, "return _resolver[\"a\"]+ _resolver[\"b\"];");

      replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return System.Math.Abs(a);");
      Assert.AreEqual(replaced, "return System.Math.Abs(_resolver[\"a\"]);");

      // The following fail with exception:
      // Unable to cast object of type 'Microsoft.CodeAnalysis.CSharp.Syntax.ElementAccessExpressionSyntax'
      // to type 'Microsoft.CodeAnalysis.CSharp.Syntax.SimpleNameSyntax'.
      replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return a.Length;");
      Assert.AreEqual(replaced, "return _resolver[\"a\"].FavouriteColour;");

      replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return a.Function(b);");
      Assert.AreEqual(replaced, "return _resolver[\"a\"].Function(_resolver[\"b\"]);");
    }

呼叫堆栈例外:

>   Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitMemberAccessExpression(Microsoft.CodeAnalysis.CSharp.Syntax.MemberAccessExpressionSyntax node) Line 1519  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.Syntax.MemberAccessExpressionSyntax.Accept<Microsoft.CodeAnalysis.SyntaxNode>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<Microsoft.CodeAnalysis.SyntaxNode> visitor) Line 1352   C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(Microsoft.CodeAnalysis.SyntaxNode node) Line 41  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitReturnStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ReturnStatementSyntax node) Line 1801    C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.Syntax.ReturnStatementSyntax.Accept<Microsoft.CodeAnalysis.SyntaxNode>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<Microsoft.CodeAnalysis.SyntaxNode> visitor) Line 7021  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(Microsoft.CodeAnalysis.SyntaxNode node) Line 41  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitGlobalStatement(Microsoft.CodeAnalysis.CSharp.Syntax.GlobalStatementSyntax node) Line 1753    C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.Syntax.GlobalStatementSyntax.Accept<Microsoft.CodeAnalysis.SyntaxNode>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<Microsoft.CodeAnalysis.SyntaxNode> visitor) Line 5970  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(Microsoft.CodeAnalysis.SyntaxNode node) Line 41  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitListElement<Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax>(Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax node) Line 168 C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitList<Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax>(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax> list) Line 145 C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitCompilationUnit(Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax node) Line 1882    C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax.Accept<Microsoft.CodeAnalysis.SyntaxNode>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<Microsoft.CodeAnalysis.SyntaxNode> visitor) Line 9061  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(Microsoft.CodeAnalysis.SyntaxNode node) Line 41  C#
    Grasshopper2.dll!RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared(string code) Line 21   C#
    Grasshopper2Tests.dll!Grasshopper2Test.ExpressionTests.TestUndeclaredReplacement() Line 31  C#
    [External Code] 
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.MethodInfoExtensions.InvokeAsSynchronousTask(System.Reflection.MethodInfo methodInfo, object classInstance, object[] parameters) Line 158  C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ExecuteInternal.AnonymousMethod__0() Line 276    C#
    Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll!Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadOperations.ExecuteWithAbortSafety(System.Action action) Line 75 C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ExecuteInternal(object[] arguments) Line 277 C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Invoke(object[] arguments) Line 129  C#
    Microsoft.VisualStudio.TestPlatform.TestFramework.dll!Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.Execute(Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod testMethod) Line 102    C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner.ExecuteTest(Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo testMethodInfo) Line 404   C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner.RunTestMethod() Line 219   C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner.Execute() Line 157 C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.UnitTestRunner.RunSingleTest(Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod testMethod, System.Collections.Generic.IDictionary<string, object> testContextProperties) Line 98    C#
    [External Code] 

”

Continuation of (Replace all variables in C# code with methods)

I need to replace all the undeclared variables in a piece of C# code with an indexer expression. For example the code:

var b = 28;
return a + b;

needs to be converted into:

var b = 28;
return _resolver["a"] + b;

The _resolver object has a string indexer which returns a dynamic. Of course this may still fail at runtime, but I want to get to the point where it at least compiles.

I've got it working for 'isolated' variables like in the example above, but it fails when a method or property on the undeclared variable is called. For example:

return a.FavouriteColour;

throws an exception in the CSharpSyntaxRewriter class. I'm probably making a syntax node substitution I'm not supposed to make, but I don't see how I should do it differently.

My rewriter code:

using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace RoslynConfusion
{
  public static class UndeclaredReplacement
  {
    public static string ReplaceUndeclared(string code)
    {
      var tree = CSharpSyntaxTree.ParseText(code);
      var compilation = CSharpCompilation.Create("Test", new[] { tree }, References);
      var rewriter = new UndeclaredRewriter(compilation.GetSemanticModel(tree));

      var root = tree.GetRoot();
      root = rewriter.Visit(root);

      return root.SyntaxTree.ToString();
    }

    private static IEnumerable<MetadataReference> References
    {
      get
      {
        yield return MetadataReference.CreateFromFile(typeof(int).Assembly.Location);
      }
    }
    private sealed class UndeclaredRewriter : CSharpSyntaxRewriter
    {
      private readonly SemanticModel _model;
      public UndeclaredRewriter(SemanticModel model)
      {
        _model = model ?? throw new ArgumentNullException(nameof(model));
      }

      public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
      {
        var nodeʹ = base.VisitIdentifierName(node);
        var symbol = _model.GetSymbolInfo(node);

        // We only care about *undeclared* identifiers...
        if (symbol.Symbol != null) return nodeʹ;
        if (symbol.CandidateSymbols.Any()) return nodeʹ;

        // ... and we don't care about unknown method invokes either.
        var parent = nodeʹ.Parent;
        if (parent.IsKind(SyntaxKind.InvocationExpression))
          return nodeʹ;

        return CreateResolverSyntax(node.Identifier);
      }
      private SyntaxNode CreateResolverSyntax(SyntaxToken token)
      {
        var literal = LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(token.ToString()));
        var indexSyntax = BracketedArgumentList(SingletonSeparatedList(Argument(literal)));
        var resolveSyntax = ElementAccessExpression(IdentifierName("_resolver"), indexSyntax);
        return resolveSyntax;
      }
    }
  }
}

And my test method with two failing cases:

    [TestMethod]
    public void TestUndeclaredReplacement()
    {
      var replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return a;");
      Assert.AreEqual(replaced, "return _resolver[\"a\"];");

      replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return a + b;");
      Assert.AreEqual(replaced, "return _resolver[\"a\"]+ _resolver[\"b\"];");

      replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return System.Math.Abs(a);");
      Assert.AreEqual(replaced, "return System.Math.Abs(_resolver[\"a\"]);");

      // The following fail with exception:
      // Unable to cast object of type 'Microsoft.CodeAnalysis.CSharp.Syntax.ElementAccessExpressionSyntax'
      // to type 'Microsoft.CodeAnalysis.CSharp.Syntax.SimpleNameSyntax'.
      replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return a.Length;");
      Assert.AreEqual(replaced, "return _resolver[\"a\"].FavouriteColour;");

      replaced = RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared("return a.Function(b);");
      Assert.AreEqual(replaced, "return _resolver[\"a\"].Function(_resolver[\"b\"]);");
    }

Call stack for the exception:

>   Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitMemberAccessExpression(Microsoft.CodeAnalysis.CSharp.Syntax.MemberAccessExpressionSyntax node) Line 1519  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.Syntax.MemberAccessExpressionSyntax.Accept<Microsoft.CodeAnalysis.SyntaxNode>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<Microsoft.CodeAnalysis.SyntaxNode> visitor) Line 1352   C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(Microsoft.CodeAnalysis.SyntaxNode node) Line 41  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitReturnStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ReturnStatementSyntax node) Line 1801    C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.Syntax.ReturnStatementSyntax.Accept<Microsoft.CodeAnalysis.SyntaxNode>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<Microsoft.CodeAnalysis.SyntaxNode> visitor) Line 7021  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(Microsoft.CodeAnalysis.SyntaxNode node) Line 41  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitGlobalStatement(Microsoft.CodeAnalysis.CSharp.Syntax.GlobalStatementSyntax node) Line 1753    C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.Syntax.GlobalStatementSyntax.Accept<Microsoft.CodeAnalysis.SyntaxNode>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<Microsoft.CodeAnalysis.SyntaxNode> visitor) Line 5970  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(Microsoft.CodeAnalysis.SyntaxNode node) Line 41  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitListElement<Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax>(Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax node) Line 168 C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitList<Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax>(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax> list) Line 145 C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitCompilationUnit(Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax node) Line 1882    C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax.Accept<Microsoft.CodeAnalysis.SyntaxNode>(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<Microsoft.CodeAnalysis.SyntaxNode> visitor) Line 9061  C#
    Microsoft.CodeAnalysis.CSharp.dll!Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.Visit(Microsoft.CodeAnalysis.SyntaxNode node) Line 41  C#
    Grasshopper2.dll!RoslynConfusion.UndeclaredReplacement.ReplaceUndeclared(string code) Line 21   C#
    Grasshopper2Tests.dll!Grasshopper2Test.ExpressionTests.TestUndeclaredReplacement() Line 31  C#
    [External Code] 
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions.MethodInfoExtensions.InvokeAsSynchronousTask(System.Reflection.MethodInfo methodInfo, object classInstance, object[] parameters) Line 158  C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ExecuteInternal.AnonymousMethod__0() Line 276    C#
    Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll!Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.ThreadOperations.ExecuteWithAbortSafety(System.Action action) Line 75 C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.ExecuteInternal(object[] arguments) Line 277 C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Invoke(object[] arguments) Line 129  C#
    Microsoft.VisualStudio.TestPlatform.TestFramework.dll!Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.Execute(Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod testMethod) Line 102    C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner.ExecuteTest(Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo testMethodInfo) Line 404   C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner.RunTestMethod() Line 219   C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner.Execute() Line 157 C#
    Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll!Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.UnitTestRunner.RunSingleTest(Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod testMethod, System.Collections.Generic.IDictionary<string, object> testContextProperties) Line 98    C#
    [External Code] 

exceptioncallstack

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文