Learn Roslyn Now: Part 15 The SymbolVisitor

I had a question the other day that I ended up taking directly to the Roslyn issues: How do I get a list of all of the types available to a compilation? Schabse Laks (@Schabse) and David Glick (@daveaglick) introduced me to a cool class I hadn’t encountered before: The SymbolVisitor.

In previous posts we touched on the CSharpSyntaxWalker and the CSharpSyntaxRewriter. The SymbolVisitor is the analogue of SyntaxVisitor, but applies at the symbol level. Unfortunately unlike the SyntaxWalker and CSharpSyntaxRewriter, when using the SymbolVisitor we must construct the scaffolding code to visit all the nodes.

To simply list all the types available to a compilation we can use the following.

In order to visit all the methods available to a given compilation we can use the following:

It’s important to be aware of how you must structure your code in order to visit all the symbols you’re interested in. By now you may have noticed that using this API directly makes me a little sad. If I’m interested in visiting method symbols, I don’t want to have to write code that visits namespaces and types.

Hopefully at some point we’ll get a SymbolWalker class that we can use to separate out our implemenation from the traversal code. I’ve opened an issue on Roslyn requesting this feature. (It seems like it’s going to be challenging to implement and would require working with both syntax and symbols).

Finding All Named Type Symbols

Finally, you might be wondering how I answered my original question: How do we get a list of all of the types available to a compilation? My implementation is below:

I should note that after implementing this solution, I came to the conclusion that it was too slow for our purposes. We got a major performance boost by only visiting symbols within namespaces defined within source, but it was still about an order of magnitude slower than the simply searching for types via the SymbolFinder class.

Still, the SymbolVisitor class is probably appropriate for one-off uses during compilation or for visiting a subset of available symbols. At the very least, it’s worth being aware of.

Learn Roslyn Now: Part 14 Intro to the Scripting API

The Scripting API is finally here! After being removed from Roslyn’s 1.0 release it’s now available (for C#) in pre-release format on NuGet. To install to your project just run:

Install-Package Microsoft.CodeAnalysis.Scripting -Pre

Note: You need to target .NET 4.6 or you’ll get the following exception when running your scripts:

Could not load file or assembly 'System.Runtime, Version=4.0.20.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.

Note: Today (October 15, 2015) the Scripting APIs depend on the 1.1.0-beta1 release, so you’ll have to update your Microsoft.CodeAnalysis references to match if you want to use all of Roslyn with the scripting stuff.

There are a few different ways to use the Scripting API.

EvaluateAsync

CSharpScript.EvaluateAsync is probably the simplest way to get started evaluating expressions. Simple pass any expression that would return a single result to this method it will be evaluated for you.

RunAsync

Not every script returns a single value. For more complex scripts we may want to keep track of state or inspect different variables. CSharpScript.RunAsync creates and returns a ScriptState object that allows us to do exactly this. Take a look:

We can also maintain the state of our script and continue applying changes to it with ScriptState.ContinueWith():

ScriptOptions

We can start to get into more interesting code by adding references to DLLs that we’d like to use. We use ScriptOptions to provide out script with the proper MetadataReferences.

This stuff is surprisingly broad. The Microsoft.CodeAnalysis.Scripting namespace is full of public types that I’m not at all familiar with and there’s a lot left to learn. I’m excited to see what people will build with this and how they might be able to incorporate scripting into their applications.

Kasey Uhlenhuth from the Roslyn team has compiled a list of code snippets to help get you off the ground with the Scripting API. Check them out on GitHub!

If you’ve got some cool plans for the scripting API, let me know if the comments below!

Learn Roslyn Now: Part 13 Keeping track of syntax nodes with Syntax Annotations

It can be tricky to keep track nodes when applying changes to syntax trees. Every time we “change” a tree, we’re really creating a copy of it with our changes applied to that new tree. The moment we do that, any pieces of syntax we had references to earlier become invalid in the context of the new tree.

What’s this mean in practice? It’s tough to keep track of syntax nodes when we change syntax trees.

recent Stack Overflow question touched on this. How can we get the symbol for a class that we’ve just added to a document? We can create a new class declaration, but the moment we add it to the document, we lose track of the node. So how can we keep track of the class so we can get the symbol for it once we’ve added it to the document?

The answer: Use a SyntaxAnnotation

A SyntaxAnnotation is a basically piece of metadata we can attach to a piece of syntax. As we manipulate the tree, the annotation sticks with that piece of syntax making it easy to find.

There are a couple of overloads available when creating a SyntaxAnnotation. We can specify Kind and Data to be attached to pieces of syntax. Data is used to attach extra information to a piece of syntax that we’d like to retrieve later. Kind is a field we can use to search for Syntax Annotations.

So instead of looking for the exact instance of our annotation on each node, we could search for annotations based on their kind:

This is just one of a few different ways for dealing with Roslyn’s immutable trees. It’s probably not the easiest to use if you’re making multiple changes and need to track multiple syntax nodes. (If that’s the case, I’d recommend the DocumentEditor). That said, it’s good to be aware of it so you can use it when it makes sense.

Learn Roslyn Now: Part 12 Document Editing with the DocumentEditor

One drawback of Roslyn’s immutability is that it can sometimes make it tricky to apply multiple changes to a Document or SyntaxTree. Immutability means that every time we apply changes to a syntax tree, we’re given an entirely new syntax tree. By default we can’t compare nodes across trees, so what do we do when we want to make multiple changes to a syntax tree?

Roslyn gives us four options:

The DocumentEditor allows us to make multiple changes to a document and get the resulting document after the changes have been applied. Under the covers, the DocumentEditor is a thin layer over the SyntaxEditor.

We’ll use the DocumentEditor to change:

to:

We’ll use the DocumentEditor to simultaneously insert an invocation before the first Console.WriteLine() and to insert another after the second.

Unfortunately there’s a ton of boiler plate when creating a Document from scratch. Typically you’ll get a Document from a Workspace so it shouldn’t be this bad:

All the familiar SyntaxNode methods are here. We can Insert, Replace and Remove nodes as we see fit, all based off of nodes in our original syntax tree. Many people find this approach more intuitive than building an entire CSharpSyntaxRewriter.

It can be somewhat difficult to debug things when they go wrong. When writing this post I was mistakenly trying to insert nodes after ifStatement.Else instead of ifStatement.Else.Statement. I was receiving an InvalidOperationException but the message wasn’t very useful and it took me quite some time to figure out what I was doing wrong. The documentation on InsertNodeAfter says:

This node must be of a compatible type to be placed in the same list containing the existing node.

How can we know which types of nodes are compatible with one another? I don’t think there’s a good answer here. We essentially have to learn which nodes are compatible ourselves. As usual the Syntax Visualizer and Roslyn Quoter are the best tools for figuring out what kinds of nodes you should be creating.

It’s worth noting that the DocumentEditor exposes the SemanticModel of your original document. You may need this when editing the original document and making decisions about what you’d like to change.

It’s also worth noting that the underlying SyntaxEditor exposes a SyntaxGenerator that you can use to build syntax nodes without relying on the more verbose SyntaxFactory.

Learn Roslyn Now: Part 11 Introduction to Code Fixes

Last time (three months ago, jeez) we talked about building our first analyzer and what we get out of the box with the default analyzer template. Today we’ll talk about the second half of the analyzer project: The Code Fix Provider.

CodeFixProvider.cs

The first thing to notice is that our class inherits from CodeFixProvider. If you take a quick look at CodeFixProvider, you’ll see that it expects you to provide at least two things:

FixableDiagnosticsIds – A list of diagnostic IDs that we would like our code fix to deal with. We would have defined these IDs in our original analyzer.

RegisterCodeFixesAsync – Registers our code fix within Visual Studio to handle our diagnostic(s).

GetFixAllProvider – An optional FixAllProvider that can apply your code fix to all the occurrences of a diagnostic.

Let’s take a look at the first half of this file:

First, we can see that we’re exporting a code fix provider for C# with the name “Analyzer1CodeFixProvider”. We can also specify additional languages such as VB if you’re writing a multi-language code fix. Note that we have to specify the name explicitly here. (Name is a property in ExportCodeFixProvider. I’d actually never come across this attribute-specific syntax before.)

To start, we’ve got the title of the analyzer which is self explanatory. We’ll expose this title to Visual Studio when we register our code fix action.

Next, we’ve got to expose a list of diagnostics for which we’d like to provide our code fix. In this case, we expose the analyzer we created in the introduction to analyzers.

Finally, the default codefix template overrides the optional GetFixAllProvider. In this case they provide a BatchFixerThe BatchFixer computes all the required changes in parallel and then applies them to the solution at one time.

Now we’ll take a look at the last two methods given to us in CodeFixProvider.cs

The first is RegisterCodeFixesAsync and it accepts a CodeFixContext. The CodeFixContext has information about where we can apply our code fix, and what diagnostics are available for us to register our code fix against. CodFixContext provides a list of diagnostics for us to choose from based on what we exposed in FixableDiagnosticIds.

Based on my experiments, RegisterCodeFixesAsync is run every time the Visual Studio light bulb appears due to a diagnostic we’ve declared interest in. At this point we can register a action to run that we’d like to apply if the user selects our code fix. We do this with context.RegisterCodeFix(). We pass in a title, a function that returns a solution with our change and an optional equivalence key. The title is simply what will be displayed to the user when they see our fix as an option. In the default template it’s “Make uppercase” which you can see below:

10a0f002fb69e0472d180c3b59ccef5a[1]

Clicking on the code fix runs MakeUppercaseAsync. There’s admittedly a lot of overhead here for what seems like a trivial change. The real work occurs in Renamer.RenameSymbolAsync() an API that quickly and easily renames symbols for us across an entire solution. Remember that Roslyn objects are immutable, so we are given an entirely new solution (newSolution) which we return from our method. Now Visual Studio will replace the previous solution with our updated copy.

One final note to make is regarding equivalenceKey. The equivalence key is used to match our code fix against other code fixes and see whether or not they’re the same. To my knowledge, there’s no commonly agreed upon format for these keys. However it looks like projects such as StyleCopAnalyzers are using a similar approach to Microsoft and name theirs with a two letter code followed by a number (eg. SA1510CodeFixProvider).
And there you have it. That’s the base case analyzer that ships with Visual Studio. Obviously we can build much more powerful analyzers and code fixes, but this project should serve as a nice starting point for most people. For more advanced analyzers check out StyleCopAnalyzers, Code Cracker or the Roslyn Analyzers.

Alive Beta Released!

Today we’re pleased to release the Alive Beta. This is our biggest release since our initial alpha launch one and a half months ago. The graduation from alpha to beta is more than just a name change and includes a bunch of new features and bugfixes.

Overhauled UI

Alive’s original UI did a poor job telling you what was going on. We’ve rebuilt it to explicitly show you what’s going on when you’re using Alive. We’ve now made it clear when Alive is working, when it has encountered an error and when execution has succeeded.

Keyboard Shortcuts

First posted on our public issue tracker by RaffaelG, this feature has been long overdue. Now you can launch Alive directly from the keyboard by pressing: Ctr-[, Ctr-[

Expanded Project Support

We’ve been trying hard to support more project types as we try to reach parity with Visual Studio. If Visual Studio can load and run it, we think Alive should be able to as well.

It’s taken a lot of work, but we’ve made progress on ASP .Net 5 projects. The Alive beta now supports DNX projects that target the full framework. We’re working hard to support .Net Core 5 as well, but it’s been difficult with a lot of this stuff being undocumented and in beta. On the bright side, we plan to have support for .Net Core 5 in our v1.0 release.

Misc Features and Bug Fixes

As usual, we’ve included a number of bug fixes and miscellaneous improvements.

  • You can now invoke static generic methods directly
  • You can now pass any parameters to static methods
  • Improved method tracking

The Full Release Notes may be found on our issue tracker.

If you’re interested in using Alive, you can purchase a license from our website.

You can update your copy of Alive from within Visual Studio or from the Visual Studio Extension Gallery.

LRN Quick Tip: Bridging Visual Studio and Roslyn

Today I was working on a sample Visual Studio extension and I had the following question:

Given a Visual Studio ITextSnapshot or ITextBuffer how can I get the corresponding Roslyn Document?

It turns out there’s a bunch of extension methods that make this easy. They’re not shipped with the Microsoft.CodeAnalysis NuGet package so you’ll need to pull them down manually:

  1. Install the EditorFeatures NuGet package:
    Install-Package Microsoft.CodeAnalysis.EditorFeatures.Text
  2. Include the appropriate a using statement:
    using Microsoft.CodeAnalysis.Text;

Now you’ll be able to map from ITextBuffer and ITextSnapshot back to Roslyn’s Document, SourceText and Workspace objects.