Roslyn… Excellent!

RoslynAwesome

 

(Blog post and mini tutorial)

So Microsoft is working on this amazing new compiler for C# and VB.NET code named “Rosyln” which will make you, your dog, your parents, and the world happy. Why? Is it faster or stronger or something? Well, yes but that’s pretty normal for new software. The news here is compiler APIs. Let me say it again…

Compiler APIs.

Okay, let that sink in. What it is useful for? Everything from re-factoring code on the fly to powering the new intellisense in Visual Studio 2013 to whatever.

Show me

Oh, so you want an example, eh? Here’s your example: Suppose you need to re-factor some code and you’re using a regular expression to parse and replace code fragments. But what if you run into some newer code that uses a “var” for example. What will you do? You could come up with a massively complex way of handling that scenario¬†or¬†you could use Roslyn. Because Roslyn exposes the compiler as an API you have access to the semantic tree and all the metadata that tree exposes.

So, ye of little faith, here’s the code already:

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Roslyn.Compilers.CSharp;
 
namespace Roslyn
{
    class Program
    {
        static void Main(string[] args)
        {
            SyntaxTree tree = SyntaxTree.ParseText(
@"using System;
using System.Collections;
using System.Linq;
using System.Text;
 
namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            var helloWorld = ""Hello, World!"";
            string xxxx = ""Happy!"";
            Console.WriteLine(helloWorld);
        }
    }
}");
 
            var compilation = Compilation.Create("Program",
                new CompilationOptions(Compilers.OutputKind.ConsoleApplication),
                new SyntaxTree[] { tree });
 
            var root = tree.GetRoot();
            var newRoot = new Rewriter(compilation.GetSemanticModel(tree)).Visit(root);
            var result = newRoot.ToFullString();
        }
    }
 
    class Rewriter : SyntaxRewriter
    {
        private readonly SemanticModel _semanticModel;
 
        public Rewriter(SemanticModel semanticModel)
        {
            _semanticModel = semanticModel;
        }
 
        public override SyntaxNode VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
		{
			if (node.Declaration.Variables.Count > 1) return node;
			if (node.Declaration.Variables[0].Initializer == null) return node;
 
			if (node.Declaration.Type.IsVar == true)
			{
				var info = _semanticModel.GetTypeInfo(node.Declaration.Type);
 
				var result = node.WithDeclaration(Syntax.VariableDeclaration(
						Syntax.ParseTypeName(info.ConvertedType.Name)
							.WithLeadingTrivia(node.Declaration.Type.GetLeadingTrivia())
							.WithTrailingTrivia(node.Declaration.Type.GetTrailingTrivia())
						).WithVariables(node.Declaration.Variables));
 
				return result;
			}
 
			return node;
		}
    }
}

Yes it’s a bit complicated but really look at it and you’ll see how amazingly simple it really is. This needs not only .NET 4.5 (Visual Studio 2012 or higher) but also the Roslyn CTP which you can get from NuGet very easily but running this in your NuGet Package Manager Console (you do have that turned on, right?)

NuGetPackageManager NuGetInstallRoslyn

Now compile, run, and examine the contents of result. You will find the var has been replaced by “String” which is what it should be. If you change the value of the “helloWorld” var in the sample code to a 1, and re-run you should see the var replaced by an Int32.

Awesome. Enjoy your trek into the wonderful world of Roslyn.

Leave a Reply