Изменение синтаксического маркера с помощью Roslyn

#c# #roslyn

#c# #рослин

Вопрос:

Я пытаюсь переписать код с помощью Roslyn. Я хочу изменить Greater Thantoken на EqualsEqualsToken. Вот мой код на данный момент:

ToParse.cs:

 public class ToParse
{
    public bool MethodToConvert(int param)
    {
        return (1 > param);
    }
}
 

Program.cs Программа.cs:

 class Rewriter : SyntaxRewriter
{
    private readonly SyntaxKind _replace;
    private readonly SyntaxKind _replacewith;
    public Rewriter(SyntaxKind replace, SyntaxKind replacewith)
    {
        _replace = replace;
        _replacewith = replacewith;
    }

    public override SyntaxToken VisitToken(SyntaxToken token)
    {
        if (token.Kind != _replace)
            return token;

        return Syntax.Token(_replacewith);
    }
}
 

Использование:

 var code = new StreamReader("ToParse.cs").ReadToEnd();
var tree = SyntaxTree.ParseText(code);
var root = tree.GetRoot();
var rewriter = new Rewriter(SyntaxKind.GreaterThanToken, SyntaxKind.EqualsEqualsToken);
var newRoot = rewriter.Visit(root);

var newTree = SyntaxTree.Create((CompilationUnitSyntax)newRoot);
var compilation = Compilation.Create("TestAssembly.dll", 
      new CompilationOptions(OutputKind.DynamicallyLinkedLibrary),
      references: new[]{ new MetadataFileReference(typeof(object).Assembly.Location)},
      syntaxTrees: new[] { newTree });
Console.WriteLine(newTree);
EmitResult res;

using (var file = new FileStream("e:\TestAssembly.dll", FileMode.Create))
   res = compilation.Emit(file);
 

После выполнения выполните консоль.WriteLine печатает измененные токены return (1 == param);
Но когда я открываю testassembly.dll с ilspy я все еще вижу return 1 > param;
Есть какие-нибудь предложения?

Комментарии:

1. вы перестроили проект?

2. ДА. Я также попробовал конфигурацию отладки / выпуска

3. Roslyn не позволяет вам переписывать во время компиляции.

4. Но он должен иметь возможность переписывать перед компиляцией, что, я полагаю, имеет место здесь.

Ответ №1:

[Примечание: вы используете немного более старую версию Roslyn. Этот ответ должен работать и для этой версии, но я могу ссылаться на классы и члены по более свежим именам, чтобы они соответствовали источнику, доступному в CodePlex.]

Исходное дерево, которое вы проанализировали, содержит BinaryExpressionSyntax узел с символом SyntaxKind of GreaterThanExpression . Когда вы меняете GreaterThanToken EqualsEqualsToken местами внутри this BinaryExpressionSyntax , он автоматически не настраивает SyntaxNode вид содержимого на . EqualsExpression

В результате вы получите a GreaterThanExpression с an EqualsEqualsToken . Поскольку это не синтаксическое дерево, которое могло быть законно сгенерировано самим компилятором, вы можете увидеть неожиданное поведение, подобное этому.

Чтобы сгенерировать правильное дерево в этом случае, я бы рекомендовал переписать сам узел вместо токена, переопределив CSharpSyntaxRewriter.VisitBinaryExpression и выполнив что-то вроде этого:

 public override SyntaxNode VisitBinaryExpression(BinaryExpressionSyntax node)
{
    if (node.CSharpKind() == SyntaxKind.GreaterThanExpression)
    {
        return SyntaxFactory.BinaryExpression(SyntaxKind.EqualsExpression, node.Left, node.Right);
    }

    return node;
}
 

Комментарии:

1. Разве какая-то часть процесса компиляции не должна проверять, что синтаксическое дерево не содержит ошибок такого рода?