Roslyn is Microsoft's "compiler as a platform" for C# and VB.NET. You can use it to read, analyze or modify existing code, or make squigglies in Visual Studio, or even generate and compile entirely new code at runtime because you're insane.
In this talk I'll introduce you to Roslyn, explain things like Workspaces, SyntaxTrees, SyntaxNodes and symbols, and show you some of the fun things you can do with it, both in console applications and as Visual Studio extensions. If you also go to a talk on Machine Learning you will probably be able to create an artificial intelligence to write code for you and take the rest of your life off.
Hi. Yes, thank you. And welcome to automate yourself out of a job with Roslyn, which is the first tech talk I've done in two years in front of people. I've been happily sitting at home room at my desk with my four or five monitors and my big, powerful PC. And so, yeah, it's been a while and I am a little bit terrified. So I'm sorry. My name is Mark Rendell and I am a. Net developer. I've been using.NET and C Sharp since the first beta of.net back in late 2000. I'm a Microsoft MVP, which doesn't matter at all, but apparently when you're speaking, you have to go, I'm a Microsoft MVP. The only reason I do the speaking is so that I get to be a Microsoft MVP and get free software and stuff. But my job, I work on something called Visual Recode, which I'll talk about more later on. But let's start off by talking about what Roslyn actually is. So the first sort of few versions of C Sharp and the first few versions of Net, the Csharp compiler and the VB.NET compiler were written in C Plus Plus. And then around the time between C Sharp Five and C Sharp six, there was a huge project to rewrite the compiler for C Sharp in C Sharp and to rewrite the compiler for VB. Net in VB. Net, which serves them right. And VB.NET programmers in the room because I'm sorry. Yeah. Not for what I'm saying. I'm just sorry. And when they rewrote the compiler for C Sharp in C Sharp, they didn't just make a new compiler, they decided to make a compiler as a service. So they built it in an open way so that all the developers in the world who were doing C Sharp work could hook into the compiler and use bits of the compiler tool chain to work with their own C Sharp code. And so essentially, we all became C Sharp compiler developers because we've got access to these parts of the C Sharp compiler, and it's completely awesome. Fundamentally, the thing it's most often used for is writing analyzers, which either install into Visual Studio or you can reference them as a Nougat package in your project. And then it adds the squiggles under lines. And if you're really lucky, it adds the fix for that as well. And we'll take a look at that later on. But I got into using Roslyn basically because Microsoft canceled WCF, which was a good move. Wcf needed to be canceled. Wcf was started in 2006, and it was basically a way to get.net to do Soap. And Soap really belongs in 2006. I mean, it doesn't belong anywhere, but if it's going to be anywhere at all, 2006 is a nice, safe place for it. And let's just leave it there. I know Java people still love it, but no. And a lot of people are very upset by this. And Microsoft said the thing we suggest that you migrate to is gRPC, which is the new sort of RPC hotness it's created by Google. Although the G does not stand for Google, nobody knows what it does stand for. It just doesn't stand for Google. And so there are a bunch of people in the world who have got tens of millions of lines of code all wrapped up behind WCF service contracts and operation contracts and data contracts. And I thought, I wonder if you could just use Roslyn to scan over all those service contracts and data contracts and generate the protobuff file that would be the equivalent gRPC service. And so I started hacking around with that and it turned out, yes, you can. I want to say it's surprisingly easy, but I also want people to buy my thing. And I actually ended up going, not only can I do that, but I can also generate the code so that we can bring across your old service contract and then just put a gRPC proxy in front of it. And so I dug really deep into Roslyn and all that sort of stuff. And so that's how I got to know about it and why I want to share it with you. Because you can use it for things like that. You could put together your own visual Recode and avoid buying $125 license, but you can also use it to sort of scan over a C Sharp object and generate the equivalent TypeScript, object or interface or class or whatever, or lots of things. So to get started with Roslyn, it's essentially just a bunch of new Get packages. And with C Sharp ten, they've gone to 4.0 on the new Get packages. And so this is what the project file would look like. We have Microsoft Code Analysis C Sharp Workspaces, and that brings in Microsoft Code Analysis C Sharp and a bunch of other things. And that contains all the types and classes and libraries that you need to start analyzing and passing and understanding and working with C Sharp code in your code. We also bring in Microsoft Code Analysis Workspaces, Ms Build. And that is the thing that hooks the Rosling compiler up to a version of Ms Bill that is installed somewhere on your machine. And that could be the Visual Studio version of Ms Bill, which is closed source and scary as hell. Or it could be the.net version of Ms Build that's hiding under programfile Netsdkasomeware. And that lets you work with. Netcore and. Net five and. Net six and all the latest versions of C Sharp. And then up at the top there we have something called Microsoft Build Locator which we'll talk about more in a minute. So at the root of everything that you do with Roslyn is workspaces or are workspaces workspaces. They're like above the solution. And you can think of a workspace as like a virtual instance of Visual Studio that's running in memory and that's taking care of all the things around the solution. And so Roslyn workspaces, you have three different kinds. You have an ad hoc workspace, which is one that you can just create and then add a solution to it, and then add projects to the solution and add documents to the projects. And that's really helpful for unit testing and for hacking around and exploring Roslyn and getting to know it. And then you have the Ms Build workspace, which is the useful one that actually hooks in with the Ms Build process and gives you useful information about the types and the code that you're working with. And then you have the Visual Studio workspace. If you install the Visual Studio Extensions option when you're installing Visual Studio, then when you're writing an extension the Vsix thing, you can get the current top level workspace from Visual Studio and work with it inside your code and below the workspace. We have a single solution which you can load from a solution file, or you can create in memory if you're using an ad hoc workspace. And a solution contains multiple projects. And those will be C Sharp projects or VB projects, but they can also be service fabric projects or random kinds of all the different projects that you can have in Visual Studio, basically. And then projects contain documents. And again, some of those documents are C Sharp documents and some of them are not. So we start out by locating a copy of Ms build and this is what Ms Build locator is for. And so I hope this code is big enough. I was so terrified of this first talk in ages that I sat last night in my hotel room and I ran through the demos and just screen grabbed them rather than trying to live code in front of you guys. So I apologize. If you love the thrill of live coding, it's not going to happen. But the first thing you do in any Rosalind project that's running from the command line. So not in Visual Studio extension. But if you're building a console application to do something funky, if you run this Msbildlocator register defaults and if you're running a net six console application, it will go off and it will find the net six version of Ms Build. If you're running a net Framework 4.8 application, it will go and find the full.net framework version of Ms build, and then everything that you do after that will work with that version of Ms Build Super handy. It doesn't have to be the first thing you call in your application, but it's a hell of a good place to put it, because if you call anything else Ms build related before you've called Ms Buildlocator register defaults, it'll just randomly choose an Ms Build environment based on alphabetical order or the position of the sun or something and you won't know what the Hell's going on. So then we create an Ms Build workspace and we can load a solution into it and we add an event handler to workspace workspace failed, so that if there are any compiler errors or problems Loading that solution, you will get them printed out there and you can see why things are not working. There is a problem with this, which I found out the very, very hard way. If you use the. Net core version of Roslyn and you try to load a full.net framework solution into it, it will not work because the Ms bill that is used for the full.net framework has a whole bunch of targets files, and those targets files reference a whole bunch of full.net framework DLLs which don't work in. Net core and it'll look like it's working. But then you'll find out that actually half the projects are showing as having no documents at all, and you can't find the things that you're looking for. And those three months that you spent refactoring the engine of your Visual RECO tool to run externally as a gRPC process because you thought that would be good, because Visual Studio is only 32 bit doesn't actually work, and you have to put it all back into Visual Studio. And then Microsoft released Visual Studio 2022, and that anyway, that's my problem. Visual Studio 2022 is at least 64 bit, so my terrible code is no longer going to cause out of memory exceptions as long as you've got 128 gigs of Ram installed in your PC. And why would you try to run Visual Studio if you don't? Okay, so we got our solution and all our projects loaded into memory. So what's the basic unit of code of C sharp code when we want to work with that in Roslyn when we start off with syntax trees. So the way the compiler works and the way most compilers work these days is you throw a load of text at it and it tokenizes it and works out which bits of the text are individual components. And those can be keywords or open braces or comments or attributes or method names or identifiers or whatever. And it turns that into a syntax tree. And syntax trees. At the top level, we have a syntax tree, and then we have a syntax node. So the tree is a collection of nodes, and every single tiny, smallest possible unit of code becomes a node in that syntax tree. And we have syntax tokens. So a keyword is a syntax token. An identifier is a syntax token. An open brace is a syntax token. And we also have syntax trivia. And this is all the stuff in your code that is not actually code. So comments are trivia, and white space is trivia, and all of these things are contained in this syntax tree. And if you have installed Visual Studio SDK, you get a really handy thing built into Visual Studio that helps you understand the syntax tree, which is called the syntax visualizer and you can go to view other. And about two thirds of the way down there is a syntax visualizer. No, one third of the way down there is a syntax visualizer option. And then you can dock that to the right side of your document. Well. And as you click around your code on the other side, you can see where that is in the syntax tree. And this is a fantastic way when you're first starting with Roslyn and you want to understand how syntax trees work, just load a little bit of code up, nice simple bit of code, and then just click around it and watch what's happening in the syntax tree on the other side. And you can see the node that you've got currently selected. And then down the bottom there's a nice property view that shows you all the properties that that node has. So that lets you get to grips with how the syntax tree works in your head and the concepts behind it. So then you want to start navigating that syntax in your code. And there are two ways of doing this. Two primary ways of doing this. And the first one, and the simplest one for most C sharp developers is to use Link, because in the same way as we have Link to XML and you can say elements, elements, elements, elements, and do a nested query down into your XML document, you can use Link and do these queries down into your syntax tree that looks like this. So we've got our solution loaded, which came from our workspace, and we passed that into our little demo function here. And we say for each VAR document in solution projects, selectnep document. So we get all the documents in the entire solution and then we say get the syntax root for this document. So that gets us the root node of the syntax tree for that document. If it's not a C sharp document, then that will come back as null. So we can just ignore it. And that could be sort of an app settings, JSON file or any of the things that are included in the project. And then we can go into root descendant nodes. So there's a root dot child nodes, which is just the first level of nodes below. The root descendant nodes is essentially an inumorable of every single node in that entire document. And so we can go through that and we can say we want to look for base type declaration syntax. So every node has a type that ends with syntax and a class declaration or an interface declaration, or an enum declaration, or a struct or a record. Now in C Sharp, nine and ten will be some type. So classes, class declaration, syntax interfaces, interface declaration, syntax. I'll leave you to imagine what the rest of them might be, but they all inherit from base type declaration syntax. So we can grab that out there, and then that type declaration syntax node has an identifier property which says this is what this thing is called, and we can use that. We can get the text property out of there. We can get the namespace by the namespace declaration syntax. If we do first ancestor ourselves, we can move back up the syntax tree looking for the first node type that matches that, and so that will be the namespace that contains the class declaration. And then we can put those two together and just write them out to the console, and that will look like this. This is like seriously meta demo here, because I'm running the application against itself, and so it's kind of enumerating through its own code and printing it out to the console line. So that works quite nicely. But you can end up with some extremely complicated link code, and you can end up doing some nasty things that are not necessarily particularly performant. And that becomes important later on when you're trying to write an Analyzer, because essentially your Analyzer Visual Studio is going to try and run it every time the user presses a key, or every time the user types a character into their document. So that's not necessarily the best way to traverse your code. And so we also have syntax Walker. And just to be absolutely clear here, there is a third type called syntax. Visitor, which is like syntax Walker, and it's easy to get them mixed up. But the difference is and it's important to remember this syntax Walker is good, and syntax. Visitor is absolutely useless. Don't use it. So with a syntax Walker, you inherit from the C sharp syntax. Walker based class. And then for every type of syntax node, it has a virtual on visit or visit method that you can override. So if I want to find class declarations, I can override visit class declaration. If I want to find interface declarations and method declarations and parameters and everything in your code, there is an override method in there for it. The really nice thing is with IntelliSense, you can just type override and then spend a happy 15 minutes scrolling up and down the IntelliSense and going, oh, yes, you can visit those. And so this gives us a very efficient way of walking across the entire syntax tree using basically the Visitor pattern. But don't use C sharp syntax. Visitor because it only goes one level, and then you don't get the things that you're expecting. So you want C sharps in tax Walker, although that is an implementation of the visitor pattern. Visitor is a bad implement anyway. So, yeah, so this is another way of doing this, and then to use this, these two screens are in the wrong order. We just create a new instance of that list types Walker, and then we go through the documents, and for each document we do get syntax root async, and then we just say Walker visit and pass that syntax document in, and it will basically go through that in roughly the same way. You can also say root accept and pass the Walker in there as well. That will work too. I have no opinion on which one of those is the best way to go, and so that produces exactly the same output as the previous demo. But technically it's a better way of working with the code. It's a more efficient and memory friendly way of working with the code. But what we've got here is just essentially the tokens wrapped in a nice tree that makes it easy to find them and navigate them and visit them and do whatever we want to do. We don't actually know what any of them mean. So we might find a token that says string with a capital S, and we might think okay, so that's going to be system string, but not necessarily. You could be working for a company that sells string, and so it has an object called string that they use all over the place. And so if it's lower case string, the keyword string, then it's probably system string. But if it's an uppercase S, then it could be the string that they sell and we don't know and trying to work that out. So going back up and finding all the using statements and seeing if you've got using system in there and does that then mean string? But is there a using string equals string code string? It becomes complicated. Fortunately, all of that is taken care of for us because this is not just a C Sharp parser, this is the whole C Sharp compiler. And so we have something called semantic models, and these are the thing that really helps you to work with your code and know what's going on. So we compile our project so we can say awaitproject, getcompilationasync, and if the project has already been compiled, that will just load stuff from the assembly, and if not, it will compile it and then load stuff from the assembly, and then we can get the model for that compilation. We can get the model for the syntax tree that we're working with from that compilation. So we've got the document and we've got the syntax tree out of it, and we can say hey, give me more intelligence, give me more information about this syntax tree. And what we work with then is symbols. So for every one of those syntax types, class declaration, syntax and method declaration, syntax, all those sorts of things, there is an equivalent symbol in the semantic model, and we can grab that. So for each of these documents we can get the syntax tree and we can compile the project, and then we can go through and we can combine this with our link queries. So we can say for this document, get me all the type declarations, and then we can pass that in to our model and say give me the declared symbol for this type declaration syntax. And that will give you an I named type symbol, which is a lot more information. So that'll tell you everything about that type and then that's got properties inside it. So you can enumerate through methods and properties and fields and constructors and everything about it, and you can get full type information namespace qualified. So you can see if something is system string or stringcompany string and work through your code like that and it makes it so much easier. So you can see here that we're getting the I named type symbol for our type declaration syntax, and then we can go through that and we can say symbol getmembers and give me the method symbols from this type symbol and that will give you all the methods public, private, internal and everything else. And then we've got this method symbol. One of the properties on there is implicitly declared. So the C Sharp compiler, bless its little heart, will generate a whole bunch of methods while it's compiling the code. So if you have a property with a getter and a setter, then the C Sharp compiler will generate two methods for that property to set the hidden field that it's created for you. And so we can use methodsymbol that is implicitly declared to throw away all the stuff that's been generated by the C Sharp compiler. And this works with async methods and various other weird things that happen in C Sharp as well. It's fascinating actually, just to go through the syntax tree or the symbol in the debugger and just explore all the ethereal stuff that the C Sharp compiler gets up to behind your back. So the output from that the autogenerated extensions has an is autogenerated method and it begins with auto generated comment method. And then we got our Roslyn demo dependency demo and so we get a lot more information this way and we can actually reason about the types that we have in our solution. There is a symbol visitor class which works a bit like the C Sharp syntax Walker class, except I tried to use it when I was first getting started with Roslyn and I found it less than useful. So I don't tend to use it a lot. But you can create a symbol visitor which looks like this and it works in exactly the same way as the syntax Walker. You pass a semantic model object like an I name type symbol or something into it and it goes through and it visits all the items below that in the symbol. But I tend to just go through the properties and sort of get members of type method, symbol and that sort of thing. So yeah, we can see here we've got a visit named type and then if we go into that we can find all the named types within our symbol, whatever that might be. And if it's the top level, then it would be like an inamespace symbol. And then within that we can revisit using the same visitor to find method symbols. So this doesn't traverse down the entire tree. You actually have to take control of it and say okay, when you find a name type, then go into it and visit it again looking for method symbols and visit property. And visit method will print things out to the console like this. And so now we can see that none of the classes I have in this demo have any properties, which is completely pointless. But yeah. So let's talk about doing something useful with this. Let's see if we can find all the types that are used by all the code in our project. So we can go in to our solution and we can say go through each project and for each project we're going to get the compilation which is the inmemory proper representation of what everything means. And then we can go through the documents and we can check to see if the document is auto generated. And this is actually really important in modern C sharp code as well, because as of csharp, eight or nine and. Net core projects in particular, you might have noticed we don't have an assembly. Info. Cs file in our. Net or.net core projects anymore because it's auto generated from the. Csproject file. But it is hiding there somewhere in your project and it will be one of the documents that get enumerated when you go to project documents. It's kind of assemblyinfo. G-C-S. And now we also have source generators which are generating random code which is also hiding in our documents tree somewhere. And we don't necessarily want to be going through those. So I've got an extension method called is. Auto generated which looks for a comment at the top of the file containing the words autogenerated or looks to see if the document extension is GCs. If you have something that is generating code and if you're into the idea of source generators and you didn't go to the talk that was in here or room five yesterday, somebody did a very good talk about source generators, which would be great to watch after this one. And if you came here thinking this was going to be about source generators, I apologize. Please feel free to complain. But yes. So if it's autogenerated, ignore it. Otherwise, get the syntax tree and then get the root of that syntax tree. Check just one more time to make sure it's really not autogenerated. And then get the semantic model. And for each of the named type symbols that we have in this syntax node, which we get here, we have name type. So we go node descendantnodes of type identifier name syntax. So that is a variable declaration or a property declaration or something like that. And we also have expression syntax which is going to be like a literal string or three plus two or a plus B or anything that's an expression and not a statement. And from that, if you have an expression that is you've got two integers called a and B, then you have an expression a plus B and the type of that expression is Int. And so you can get that main type symbol back out and say this is an integer expression and then we can return those back and then we can do this which shows us every type that is used by any of those identifiers or expressions in your entire project. And you could use this, for example to go am I using anything from System link. Xml? And if I'm not, then I can remove the reference to System link. Xml from my project. But the fun bit comes when you start rewriting code, when you want to make changes to code, or you want to generate a new version of code, when you're going through net four code and looking for things that won't work in. Net six and putting comments around it to say don't do this or putting ifdefs around it, all that sort of thing. And reroute in Roslyn at first is extremely challenging because there's a feature that's built into Roslyn that was a fundamental design goal right from the start and that is immutability. So things in Roslyn are immutable. Syntax nodes are immutable. If you change them, it becomes a new syntax node. And this goes right the way through the whole compiler project. What things in Roslyn are immutable? Everything, literally everything. Anything that you change will create a new version of that thing, which you then have to say this is now the actual version of this thing that I want to use. So the solution, if you change a syntax node in a document somewhere, you have created a new version of the solution right at the top level. God knows what this does to memory. It horrifies me, but everything in here is immutable. Does anybody use system collections? Immutable? The Nougat package. It's super handy. You got immutable dictionaries and immutable arrays and immutable lists and immutable hash maps, and every list or array anywhere in Roslyn is going to be one of those collection types. That entire new get package was basically created by the Roslin team because they needed it for their compiler. And so that makes it quite difficult if you're just linking or visiting through your nodes, if you change something, how do you make that flow back? So that that becomes the canonical version of the solution. And so we don't work in that way when we're trying to rewrite code. We use the C Sharp syntax rewriter. And this is just like the C Sharp syntax Walker. But where the C Sharp syntax Walker? All the methods return void in the C Sharp syntax rewriter. All the methods return a syntax node. So you take a syntax node in and if you want to change it, then you modify it and you return your modified syntax node back out of the method. If you don't want to change it, you return the original note that you were passed in, or actually you return base visit literal expression because you never know what's going on in the base method. Might be nothing. Always best to call it. And if you want to remove the syntax node entirely, if you just go no, that's a terrible syntax mode. I don't like it. I want it to go away. Then you can return null and it will disappear forever. And if you do that accidentally and then ship the code that does it, you can make people very, very, very unhappy. I know. So this is how we do this from the top level. We go through our projects and we get our compilation and then we go through our documents and we get our syntax route and then we create a string literal upshifter, which is the most useful utility class in the entire world. It will just go through your entire C Sharp project and it will find any literal strings you have anywhere in the whole thing and it will add to upper invariant at the end of them. And then your application will become Shouty. I am thinking of releasing this as an actual Visual Studio extension and just calling it Shouty and seeing how many people download it and install it. That could be fun. I only have one Visual Studio. I've got two Visual Studio extensions. One is Visual Recode. The other, which sadly doesn't work anymore, was just called no Git and all it did. It's impossible to turn off the Git integration in Visual Studio, and all it did back in Visual Studio 2015 was every time it got turned on, it turned it off again. It was surprisingly popular. But anyway, back to this. So we get a semantic model and then we create a new string literal upshifter. So this is a syntax Walker, but we want to have the information about the semantic model passed into it. And so when you're creating a C Sharp syntax Walker or a C Sharp syntax rewriter, you get the model and then you pass it in when you create the rewriter or the visitor or the Walker and then it can use it internally and then you just say create a new route by passing this route into that syntax rewriter. And then when you get that back there is equivalent to method that will compare two syntax trees and work out if they are the same, because you might get the same syntax tree back, you might sort of think you've made changes, but actually you haven't. And so use this is equivalent to to say, are these basically the same code? If it's not, then we actually have to call solution withdocument syntax root which will update the solution, update the project, and make the new solution the one that includes those changes. And then at the end we have to say if the new solution is not the same as the solution that is in the current workspace, then we call workspace, try apply changes and pass a new solution in. And this gets really fun because if some other code running on some other thread at the same time has also made changes to the solution which are incompatible with yours. This is essentially like a Git merge and you could have conflicts. And so when you call workspace, try apply changes. What you should be doing is checking the boolean result, and if it's false, that means the changes you're trying to make conflict with the changes that some other bit of code has made somewhere. And what you need to do is go right back up to the top and start again with the current solution and hope that it doesn't happen the second time through three or four times around. That loop is usually enough, depending on how many extensions and how many analyzers you have included in your project. So that would take this code here and turn it into this code here. And that's basically it. That's a syntax rewriter. I will put all this code up onto GitHub at the end of the talk so that you can actually go and find it and do useful things with it. So that's fun. And you can write code that generates C sharp code, and you can write code that reads C sharp code and then generate other things based on it. And you can do fun stuff with that. If you don't like DocFX, then you could write your own Doc. Fx by going through and finding the dock comments on all your members. But the point at which this becomes really useful is with Rosalind Analyzers, which I mentioned at the start. So you might notice these days if you use X unit, it comes with its own squiggles and it tells you that if you've got a theory and then you've got inline parameters and the number of arguments you put in the inline parameters doesn't match the number of arguments in your test method, you'll get a squiggle that pops up under the theory that says this isn't going to work and there's a whole bunch of stuff in there. And most Microsoft projects, ASP. Net core comes with a bunch of analyzers that help you ASP. Net core properly. Entity Framework core comes with a bunch of analyzes that help you do that properly. Weirdly enough, Roslyn comes with a bunch of analyzers that pops up squiggles and says you're doing Roslyn wrong. We've used Roslyn to work out that you're doing Roslyn wrong. How meta would you like this to get? So the really helpful thing with this, if you work in a company where you've got some shared code, you've got something that you do a lot and you've written a nice utility library to wrap around it to help you work with that, or you've got a shared repository project or whatever, you can write your own analyzers and include them in the same NuGet package as a dependency. And then when people download your library and try to use it, you can help them. You can be there looking over their shoulder and going, oh no, that's not the right way to do that. I was working at one of the large banks and working in a WPF application and we were trying to work out why the hell it was so slow. And the answer turned out to be because we wrote it in WPF, obviously, but we thought we should do what we can to try and make it faster. And so I spent a lot of time instrumenting the entire application so that we could see how long various things that were happening as Windows were created was. And to do this, we were one team and there were about 10,000 developers at this bank and about two and a half thousand of them use.net. And if you want to introduce a new package like At Metrics, for example, into that ecosystem, that's about six months of meetings and filling out forms and putting a case together and everything else. If you want to bring in something from System, something from Microsoft, that's no problem at all. And Microsoft, as part of the. Net core project, created this thing called System Diagnostics Diagnostics Source, which essentially let you track activities and create events and everything else. And then you can just read those out and write them to wherever you want to write them to an Influx database or Prometheus or something like that. And so I introduced this idea of using diagnostic source. So you work with that. You create a diagnostic listener and when you call it, you just write a string to identify what your event was, and then an anonymous object which contains effectively all the Tags and useful information and the name of the window and the thread ID and all that sort of stuff that you might want. And the key thing here is we have a WPF application and we're trying to make it faster and I want to add code and so people are going that's going to slow it down. And so Diagnostic Source has this thing where if you've attached a listener to it, then it will do something, but if you haven't attached a listener to it, then you can just ignore it. And so doing this is wrong because every time this static void bar gets called, we're going to create a new anonymous object and set eight to 42. We don't want to do that. What we want to do is check to see if the diagnostic source is actually enabled. If something is listening to it and is listening for the event we're about to post, then we call diagnostic right now. This is a nightmare. Who uses Microsoft Extensions logging in their code who every time you call underscorelogger loginformation prefixes that with Iflogger is enabledblog, level information, bad people, bad. You should if you look in the.net core code, if you look in the ASP. Net core code, they do that. Actually, Net Six people are so terrible at this that. Net Six now includes source generators. So you can create a log method and it will generate that boilerplate code for you and prevent allocations and prevent logging from happening at all and creating objects and doing all this sort of stuff. And so I wanted something like that for this diagnostic source so that when you said diagnostics, write something, it would do the little squiggle and say you should check to see if this source is enabled and ideally be able to add that bit of code to say if diagnostic source is enabled. So if you go into Visual Studio project templates, you will find one of them is Analyzer with codefix. Net standard. These are the Rosalind based templates and Analyzer with codefix. Net standard will create a project that has your Analyzer and a unit test project so you can test your Analyzer and a Vsix project. So if you want to, you can package your Analyzer up as a Visual Studio extension and distribute it through the marketplace or just email a Vsix to your friends. And an Analyzer has two important components. One of them is the diagnostic Analyzer. That's the thing that looks at the code and returns something out to say that's not right, you've done it wrong. You're the worst programmer ever. You should have been a farmer and the code fix provider, which is the thing that jumps in for you and goes, never mind farming is a dying industry. Anyway, let's see if we can help you get this right. So this is an Analyzer. It has a crap load of boilerplate code. The thing I really like about it is that the boilerplate code causes squiggles. So the boilerplate code for writing an Analyzer is triggering the Analyzer that's analyzing the boilerplate. Like I say, this gets really but no. So we have a diagnostic descriptor which says this is the description, this is what this thing is looking for. We've got some localizable strings which I've collapsed there because I don't localize my code, just speak English. And we have a list of supported diagnostics so that we can tell essentially the Visual Studio runtime or the Rider run time. Or by the way, Pro Tip, who uses Visual Studio code, who misses all the helpful sort of things from ReSharper or Visual Studio when you're editing C Sharp code. So if you go into the settings in the C Sharp extension settings in Visual Studio code, there is a checkbox which is not checked by default that says Enable analyzes. And if you turn that on, you get extract method and initialize read only field and all the things that you have in Visual Studio. Apparently the reason it's turned off is because it uses a bit of memory. But we're developers, we've got memory to spare. Unless we work for a big enterprise company who doesn't know why we can't use the same spec of machine to develop the software that other people use to run the software. And if that's you, then I feel your pain. Then we get down to the bit that actually does something useful. We have our analyzed node method and in here we can look at the node that we get from our analysis context and we can say is this a diagnostic source method invocation is it calling right on a diagnostic source? And we're going to pass in our semantic model which we're also getting from the context there, which is coming from the current compilation of the project. And then we can call another method that says is this guarded? Is there an enabled check around it anywhere? And if it's not, then we can return a diagnostic that will then generate the handy green or red squiggles and we can control that with this diagnostic severity warning. So if you do diagnostic severity warning, that's green squiggles. If you do diagnostic severity error, that's red squiggles. So our is guarded method looks like this, we can look at the expression and say is that a literal expression syntax? So are we calling is enabled on the literal? Otherwise do we have a matching symbol that is enabled called on it matching symbol exists. Can just look back up the parents of our current syntax node and see if any of them are an if statement and if they are an if statement, then is the expression inside that if statement acting on our diagnostic source symbol? And if it is, is it calling the is enabled? So we got this diagnostic source method invocation is enabled and if we don't find that anywhere between our diagnosticsours right and the top of wherever we get to. So like the method declaration or property declaration, then we're going to say if we find it, we say yes, it's being called. Otherwise we just return false out of this similar thing. When we're looking with literals going back up and we have to match symbols and literals because the node that we're looking at could be either of those and then so that will generate the green squiggles saying hey, you're using diagnostics source right, without any enable check. So that's told the programmer that they're a bad person and should rethink their life choices. And then we have the code fix provider and these are a little bit more complicated to write, but essentially we are going to find so we register our code fixes. We say these are the diagnostic source or the diagnostic warnings. It was a bad idea to use this because I'm talking about Visual Studio diagnostics on one hand and then this diagnostic source on the other hand. I'll fix this for the next time I do this talk. So, yeah, we basically say we have a code fix for when you get that warning saying diagnostic source should be guarded with an Is enabled call. And so we can register our code fix here, and then we have this bit of code here and it's quite dense. But I think this really highlights the fact that Roslyn is incredibly powerful and enables you to do some really quite clever things with a fairly minimal amount of code and without really having to know too much about compilers and syntax trees and everything else. Because this is a method that sits on a single screen at 1920 by 1080 with a reasonable font size. And this is enough to wrap that diagnosticsource right, with an if diagnostic source is enabled. So we just say we've got an expression which is going to be our diagnosticssource, right? And we can just get the leading trivia off it, whatever that might be, which is probably white space. And then into at the end of the white space we can just add if diagnostics source is enabled going to step out here and use the laser pointer to point out where these things are happening. It's very precarious up here. So yes, so we got Is enabled here. We're going to pass syntax factory. So this is what lets us generate C sharp code in our code fix. And so we're going to pass this expression and we're just going to say source is enabled. So source is whatever the identifier for our diagnostic source instance was. And so we can just pass this and say source is enabled and that will give us an invocation expression syntax. And then we can replace the argument list in that which will be empty with a new argument list which adds in the argument which was the first thing passed to our diagnostic source writing vacation. So in this case that would be bar, and then we can create a new if statement. And so this past expression lets us turn a C sharp string into a C sharp syntax node. But syntax factory. Also, you can just say create an if statement and the if keyword should be this token syntaxkind if keyword. And then we're going to add an open parent token because we need that. And then we're going to put our Is enabled expression inside that there. And then we're going to have our close parent token and add on trailing trivia new line, and then we can indent the expression underneath that. I don't know what that else does. Don't worry about it. And then we're going to add back on whatever the leading trivia was on this thing before we started digging around with it, which should give us the same level of indentation as we had before. Then we can just go, hey, give us the solution from the document that we're looking at and give us the syntax root from this document, and then we can replace the node that was our old expression up here with our new if statement, and then we can do that same thing again and say the new solution is the original solution with this change to our document syntax, and then return the new solution back. So yeah, there's a lot going on and it's not the simplest thing in the world, but for what it's doing and how easily that integrates with Visual Studio and is able to provide helpful tweaks to your code. As you're going along, you can see that it's quite a powerful tool that you can use, and you can use this. Like I say, you can ship analyzers with code fixes, with shared projects internally, with stuff with new Get packages that you publish on your internal new Get package server. If you have particular styles of coding that you like to enforce, then you can use this to enforce those coding styles. You can say this is an error and these analyzes will also be run during the compilation process and they will put their warnings and errors into the compiler output. If you're one of those places where they're not allowed to use the VAR keyword, then you could write an Analyzer that looks for the VAR keyword and reports that as an error, and then add things to source control so that you can't check in code that won't compile. And it won't compile because you've got an Analyzer in there that says the Varkey word isn't allowed. Because why don't people I never understand why people don't like the VAR key word. It's great. But yeah, you'll also have a test project generated. It will be an Ms test project for reasons, but it makes it very easy to test that your Analyzer is going to work. And so you just have your test fixture which inherits from code fix verifier, which includes a bunch of methods that are helpful to make sure that your diagnostic is working. And so you can say verify C Sharp diagnostic on the test string there and make sure that doesn't return any diagnostics. If you've got an empty string, then you're definitely not calling diagnostics source, right? So that's not going to show any diagnostics. You can then pass in a method that does call diagnosticsa, right? And you can check that the diagnostic is triggered, and so you can make sure that your Analyzer is working with the code that is important to you. And then you can also check the fixed code is working by passing in the code that should trigger the diagnostic and the fix, and then also the code that is what it should look like after the code fix has been run. This is super handy. This is so much easier because the other way of testing this is to Press F Five and have it launch a new Visual Studio instance with that Analyzer switched on, and then type code inside it and make sure it triggers and all this sort of stuff. It's a damn good thing I decided to do this. Let's run through all the demos and screen, grab them and just turn them into slides. Because when I tried to run this project last night, it didn't because it was written for Visual Studio 2019 and I've only got Visual Studio 2022 preview one installed on this laptop and it just didn't work. But trust me, if I had Visual Studio 2019, I could demo it right now and it would work brilliantly. But you'll just have to take my word for it. Yes, here we have this is what it should look like. And then we can just call Verify C Sharp fix, which is a method provided by that code fix Verifier Base class that says yeah, after I apply the diagnostic code fix, this is what the new code looks like. So that's basically it. And I've done that. I never get to the end of my talks with two minutes to spare. Hang on, I'll think of a tangent to go off on for five minutes. So yeah, no, if you want to learn more about this, the thing I found super useful when I was starting out and trying to understand why my C Sharp syntax visitor wasn't doing what I wanted it to do and that I should be using C Sharp Syntax Walker instead. Joshvarty.com Learnroslinnow was the resource I used to get my head around all of this sort of stuff. Also on GitHub, there is a net analyzers organization that has a whole bunch of refactorings and code fix providers and analyzers that you can use to crib from and copy and paste from. And I'm writing a song about that. If you have a. Net for application and you're looking to migrate to. Net core, then please take a [email protected] It's a work in progress, but it will currently take your WCF application and turn it into a gRPC application, and we're working on making it do useful things with MVC and Web API and other things as well. And that is basically it. I hope that was useful. I hope that's given you some ideas that you can go off and start hacking on and do fun stuff with later on today at 07:00 in room three, I will be hosting some fun talks ahead of the party, which is just speakers talking about things that they wouldn't be allowed to talk about in an actual talk. And then the band Dylan BT and the line breakers will be on later, and I'll be singing because I got drunk once and went, yeah, all right. So yeah, come to that. And last slot tomorrow, if you're still around at the end of the conference. Not sure what room it's in, but I'm doing a completely stupid talk about the worst programming language ever, which is lots of fun. So if your brain is melted by the end of the conference. Then come along to that. Other than that. 5 seconds to go. I've been Mark Rendell. Thank you very much for coming. And I'll see you again. Cheers.