用调用替换C#代码中的未申报变量
延续(用方法)
我需要用索引器表达式将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]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论