Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Rosyln to rewrite switch blocks to if/else

Tags:

c#

roslyn

I'm new to roslyn so I'm looking for some pointers or sample code to start doing what I want.

I have a lot of code that is similar to this (it was generated by a tool)

switch (boolVariable)
{
    case false:
    {
        str = "blahblah";
        break;
    }
    case true:
    {
        str = "somethingelse";
        break;
    }
    default:
    {
        str = "ughthiswouldnevergethit";
        break;
    }
}

What I want to do is detect and then rewrite the syntax to something that is more efficient and developer friendly

if(boolVariable)
{
   str = "somethingelse";
}
else
{
   str = "blahblah";
}

Essentially I am wanting to optimize out all switches that are on boolean values.

like image 387
Matt Avatar asked Apr 08 '15 21:04

Matt


1 Answers

You have to use CSharpSyntaxRewriter, below is the code to achieve that (with simple case, you can extend from that)

(You should download Syntax Visualizer extension or go to a simple online version CSharp SyntaxTree in order to understand more about Syntax Tree)

string code = ReadText("Case6.cs"); // get source code as string for a class
var tree = CSharpSyntaxTree.ParseText(code);
CompilationUnitSyntax root = (CompilationUnitSyntax)tree.GetRoot();
Compilation compilation = CSharpCompilation.Create("assembly", syntaxTrees: new[] { tree }, references: new[]
{
    mscorlib = MetadataReference.CreateFromAssembly(typeof(object).Assembly),
               MetadataReference.CreateFromAssembly(typeof(Stack<>).Assembly)
});

var semanticModel = compilation.GetSemanticModel(tree);
var myRewriter = new MyRewriter(semanticModel);
var result = myRewriter.Visit(tree.GetRoot());
var str = result.ToString();

// below are classes for Rewriter
public class MyRewriter : CSharpSyntaxRewriter
{
    private SemanticModel semanticModel;

    public MyRewriter(SemanticModel semanticModel)
    {
        this.semanticModel = semanticModel;
    }

    public override SyntaxNode VisitSwitchStatement(SwitchStatementSyntax node)
    {
        var result =  base.VisitSwitchStatement(node);

        // detect your case:
        // first check if the expression in switch is bool type?
        var typeInfo = semanticModel.GetTypeInfo(node.Expression);

        if(typeInfo.Type.SpecialType != SpecialType.System_Boolean)
        {
            return result;
        }

        // okie we make the conversion here
        // find true statement
        var trueSection = node.Sections
                .First(f => f.Labels.First().ToString().Contains("true"));
        var falseSection = node.Sections
                .First(f => f.Labels.First().ToString().Contains("false"));

        var trueStatement = trueSection.Statements.Count == 1
                        ? trueSection.Statements.First()
                        : SyntaxFactory.Block(trueSection.Statements);
        var falseStatement = falseSection.Statements.Count == 1
                        ? falseSection.Statements.First()
                        : SyntaxFactory.Block(falseSection.Statements);

        var ifStatement = SyntaxFactory.IfStatement(node.Expression,
            trueStatement,
            SyntaxFactory.ElseClause(falseStatement));

        //NOTE that: this class will remove all break statements
        // it will not be correct if break in loop, you can base on that to 
        // write more accurately
        var breakRemover = new BreakRemover();

        result = breakRemover.Visit(ifStatement);

        return result;
    }
}

public class BreakRemover : CSharpSyntaxRewriter
{
    public override SyntaxNode VisitBreakStatement(BreakStatementSyntax node)
    {
        // should add further check to make sure break statement is in
        // switch, the idea is find closest ancestor must be switch (not
        // for, foreach, while, dowhile)
        return SyntaxFactory.EmptyStatement();
    }
}
like image 140
nhabuiduc Avatar answered Nov 05 '22 16:11

nhabuiduc