{"id":411,"date":"2020-05-12T10:25:00","date_gmt":"2020-05-11T21:25:00","guid":{"rendered":"https:\/\/blog.wiseowls.co.nz\/?p=411"},"modified":"2021-09-19T02:35:24","modified_gmt":"2021-09-18T13:35:24","slug":"walking-code-with-roslyn","status":"publish","type":"post","link":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/","title":{"rendered":"Getting started with Roslyn code analysis"},"content":{"rendered":"\n<p>It was going to happen eventually &#8211; our research on C# dynamic features eventually ended up with an attempt to parse bits of source code. There are <a href=\"https:\/\/stackoverflow.com\/a\/81427\/12339804\">quite a few solutions<\/a> on the market, with NRefactory being our preferred tool over the years. There are however a few limitations: it does not support .NET core and C# 6. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">It is a big deal<\/h2>\n\n\n\n<p>It might seem, that support for newer language spec is not critical. But in fact, it gets problematic very quickly even in more established projects. Luckily for us, Microsoft has chosen to open source <a href=\"https:\/\/github.com\/dotnet\/roslyn\">Roslyn<\/a> &#8211; the very engine that powers their compiler services. Their official documentation covers the platform pretty well and goes in great detail of writing Visual Studio code analysers. We however often have to deal with writing MSBuild tasks that load the whole solution and run analysis on class hierarchies (for example, to detect whether a single `SQL SELECT` statement is being called inside a <code>foreach<\/code> loop &#8211; we would fail the build and suggest to replace it with bulk select)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installing<\/h2>\n\n\n\n<p>Roslyn is available via NuGet as a number of <strong>Microsoft.CodeAnalysis.*<\/strong> packages. We normally include these four:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">Install-Package Microsoft.CodeAnalysis.Workspaces.MSBuild\nInstall-Package Microsoft.CodeAnalysis\nInstall-Package Microsoft.CodeAnalysis.CSharp\nInstall-Package Microsoft.Build # these classes are needed to support MSBuild workspace when it starts to load solution\nInstall-Package Microsoft.Build.Utilities.Core # these classes are needed to support MSBuild workspace when it starts to load solution\nInstall-Package Microsoft.Build.Locator # this is a helper to locate correct MSBuild toolchain (in case the machine has more than one installed)<\/code><\/pre>\n\n\n\n<p>Sometimes the environment gets confused as to what version MSBuild to use, and this is why starting a project with something like this is pretty much a must since VS2015: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">\/\/ put this somewhere early in the program\nif (!MSBuildLocator.IsRegistered) \/\/MSBuildLocator.RegisterDefaults(); \/\/ ensures correct version is loaded up\r\n{\r\n    var vs2022 = MSBuildLocator.QueryVisualStudioInstances().Where(x => x.Name == \"Visual Studio Community 2022\").First(); \/\/ find the correct VS setup. There are namy ways to organise logic here, we'll just assume we want VS2022\r\n    MSBuildLocator.RegisterInstance(vs2022); \/\/ register the selected instance\r\n    var _ = typeof(Microsoft.CodeAnalysis.CSharp.Formatting.CSharpFormattingOptions); \/\/ this ensures library is referenced so the compiler would not try to optimise it away (if dynamically loading assemblies or doing other voodoo that can throw the compiler off) - probably less important than the above but we prefer to follow cargo cult here and leave it be\r\n}<\/code><\/pre>\n\n\n\n<p>After initial steps, simplistic solution traversal would look something along these lines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">async Task AnalyseSolution()\n{\n\tusing (var w = MSBuildWorkspace.Create())\n\t{\n\t\tvar solution = await w.OpenSolutionAsync(@\"MySolution.sln\");\t\t\n\t\tforeach (var project in solution.Projects)\n\t\t{\t\t\t\n\t\t\tvar docs = project.Documents; \/\/ allows for file-level document filtering\n\t\t\tvar compilation = await project.GetCompilationAsync(); \/\/ allows for assembly-level analysis as well as SemanticModel \n\t\t\tforeach (var doc in docs)\n\t\t\t{\n\t\t\t\tvar walker = new CSharpSyntaxWalker(); \/\/ CSharpSyntaxWalker is an abstract class - we will need to define our own implementation for this to actually work\n\t\t\t\twalker.Visit(await doc.GetSyntaxRootAsync()); \/\/ traverse the syntax tree\n\t\t\t}\n\t\t}\n\t}\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Syntax Tree Visitor<\/h2>\n\n\n\n<p>As with pretty much every single mainstream syntax analyser, the easiest way to traverse syntax trees is by using a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Visitor_pattern\">Visitor Pattern<\/a>. It allows to decouple tree nodes and processing logic. Which will allow room for expansion on either side (easy to add new logic, easy to add new tree node types). Roslyn has stub <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/microsoft.codeanalysis.csharp.csharpsyntaxwalker?view=roslyn-dotnet\">CSharpSyntaxWalker<\/a> that allows us to only override required nodes for processing. It then takes care of everything else. <\/p>\n\n\n\n<p>With basics out of the way, let&#8217;s look into classes that make up our platform here. Top of the hierarchy is MSBuild <code>Workspace<\/code> followed by <code>Solution<\/code>, <code>Project<\/code> and <code>Document<\/code>. Roslyn makes a distinction between parsing code and compiling it. Meaning some analytics will only be available in <code>Compilation<\/code> class that is available for project as well as for individual documents down the track.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Traversing the tree<\/h2>\n\n\n\n<p>Just loading the solution is kind of pointless though. We&#8217;d need to come up with processing logic &#8211; and the best place to do it would be a <code>CSharpSyntaxWalker<\/code> subclass. Suppose, we&#8217;d like to determine whether class constructor contains <code>if<\/code> statements that are driven by parameters. This might mean we&#8217;ve got overly complex classes and could benefit from refactoring these out:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public class ConstructorSyntaxWalker : CSharpSyntaxWalker\r\n{\r\n    public List&lt;IParameterSymbol> Parameters { get; set; }\r\n    public int IfConditions { get; set; }\r\n    \n    bool processingConstructor = false;\r\n\r    SemanticModel sm;\r\n\r\n    public ConstructorSyntaxWalker(SemanticModel sm)\r\n    {\r\n        this.sm = sm;\r\n        Parameters = new List&lt;IParameterSymbol>();\r\n    }\r\n\r\n    public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax node)\r\n    {\r\n        processingConstructor = true;\r\n        base.VisitConstructorDeclaration(node);\r\n        processingConstructor = false;\r\n    }\r\n\r\n    public override void VisitIfStatement(IfStatementSyntax node)\r\n    {\r\n        if (!processingConstructor) return; \/\/ we only want to keep traversing if we know we're inside constructor body\r\n        Parameters.AddRange(sm.AnalyzeDataFlow(node).DataFlowsIn.Cast&lt;IParameterSymbol>()); \/\/ .AnalyzeDataFlow() is one of the most commonly used parts of the platform: it requires a compilation to work off and allows tracking dependencies. We could then check if these parameters are supplied to constructor and make a call whether this is allowed \r\n        IfConditions++; \/\/ just count for now, nothing fancy\r\n        base.VisitIfStatement(node);\r\n    }\r\n}<\/code><\/pre>\n\n\n\n<p>Then, somewhere in our solution (or any other solution, really!) We have a class definition like so:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public class TestClass\r\n{\r\n    public TestClass(int a, string o) \r\n    {\r\n        if (a == 1) DoThis() else DoSomethingElse();\r\n        if (o == \"a\") Foo() else Bar();\r\n    }\r\n}<\/code><\/pre>\n\n\n\n<p>If we wanted to throw an exception and halt the build we could invoke out <code>SyntaxWalker<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public static async Task Main()\n{\n    await AnalyseSolution();\n}\n...\nasync static Task AnalyseSolution()\r\n{\r\r\n    using (var w = MSBuildWorkspace.Create())\r\n    {\r\n        var solution = await w.OpenSolutionAsync(@\"..\\..\\..\\TestRoslyn.sln\"); \/\/ let's analyse our own solution. But can be any file on disk\r\n        foreach (var project in solution.Projects)\r\n        {\r\n            var docs = project.Documents; \/\/ allows for file-level document filtering\r\n            var compilation = await project.GetCompilationAsync(); \/\/ allows for assembly-level analysis as well as SemanticModel \r\n            foreach (var doc in docs)\r\n            {\r\n                var walker = new ConstructorSyntaxWalker(await doc.GetSemanticModelAsync());\r\n                walker.Visit(await doc.GetSyntaxRootAsync()); \/\/ traverse the syntax tree\r\n                if (walker.IfConditions > 0 &amp;&amp; walker.Parameters.Any()) throw new Exception(\"We do not allow branching in constructors.\");\r\n            }\r\n        }\r\n    }\r\n}<\/code><\/pre>\n\n\n\n<p>And there we have it. This is a very simplistic example, but possibilities are endless!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It was going to happen eventually &#8211; our research on C# dynamic features eventually ended up with an attempt to parse bits of source code. There are quite a few solutions on the market, with NRefactory being our preferred tool over the years. There are however a few limitations: it does not support .NET core &hellip; <a href=\"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Getting started with Roslyn code analysis&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":420,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[11],"tags":[12],"class_list":["post-411","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dev","tag-c"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Getting started with Roslyn code analysis - Timur and associates<\/title>\n<meta name=\"description\" content=\"In this article we will build a quick test bench to walk C# code using Roslyn, open source compiler API from Microsoft.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Getting started with Roslyn code analysis - Timur and associates\" \/>\n<meta property=\"og:description\" content=\"In this article we will build a quick test bench to walk C# code using Roslyn, open source compiler API from Microsoft.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/\" \/>\n<meta property=\"og:site_name\" content=\"Timur and associates\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-11T21:25:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-09-18T13:35:24+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blog.wiseowls.co.nz\/wp-content\/uploads\/2020\/03\/office-working-app-computer-97077_cr-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"725\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"timur\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@TimurKh\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/\"},\"author\":{\"name\":\"timur\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#\\\/schema\\\/person\\\/34d0ed30d573b5bc317ea990bd2e0c59\"},\"headline\":\"Getting started with Roslyn code analysis\",\"datePublished\":\"2020-05-11T21:25:00+00:00\",\"dateModified\":\"2021-09-18T13:35:24+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/\"},\"wordCount\":485,\"image\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/wp-content\\\/uploads\\\/2020\\\/03\\\/office-working-app-computer-97077_cr-scaled.jpg\",\"keywords\":[\"c#\"],\"articleSection\":[\"Development\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/\",\"url\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/\",\"name\":\"Getting started with Roslyn code analysis - Timur and associates\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/wp-content\\\/uploads\\\/2020\\\/03\\\/office-working-app-computer-97077_cr-scaled.jpg\",\"datePublished\":\"2020-05-11T21:25:00+00:00\",\"dateModified\":\"2021-09-18T13:35:24+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#\\\/schema\\\/person\\\/34d0ed30d573b5bc317ea990bd2e0c59\"},\"description\":\"In this article we will build a quick test bench to walk C# code using Roslyn, open source compiler API from Microsoft.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/#primaryimage\",\"url\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/wp-content\\\/uploads\\\/2020\\\/03\\\/office-working-app-computer-97077_cr-scaled.jpg\",\"contentUrl\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/wp-content\\\/uploads\\\/2020\\\/03\\\/office-working-app-computer-97077_cr-scaled.jpg\",\"width\":2560,\"height\":725},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2020\\\/05\\\/12\\\/walking-code-with-roslyn\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Getting started with Roslyn code analysis\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#website\",\"url\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/\",\"name\":\"Timur and associates\",\"description\":\"Notes of an IT contractor\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#\\\/schema\\\/person\\\/34d0ed30d573b5bc317ea990bd2e0c59\",\"name\":\"timur\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg\",\"caption\":\"timur\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/TimurKh\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Getting started with Roslyn code analysis - Timur and associates","description":"In this article we will build a quick test bench to walk C# code using Roslyn, open source compiler API from Microsoft.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/","og_locale":"en_US","og_type":"article","og_title":"Getting started with Roslyn code analysis - Timur and associates","og_description":"In this article we will build a quick test bench to walk C# code using Roslyn, open source compiler API from Microsoft.","og_url":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/","og_site_name":"Timur and associates","article_published_time":"2020-05-11T21:25:00+00:00","article_modified_time":"2021-09-18T13:35:24+00:00","og_image":[{"width":2560,"height":725,"url":"https:\/\/blog.wiseowls.co.nz\/wp-content\/uploads\/2020\/03\/office-working-app-computer-97077_cr-scaled.jpg","type":"image\/jpeg"}],"author":"timur","twitter_card":"summary_large_image","twitter_creator":"@TimurKh","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/#article","isPartOf":{"@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/"},"author":{"name":"timur","@id":"https:\/\/blog.wiseowls.co.nz\/#\/schema\/person\/34d0ed30d573b5bc317ea990bd2e0c59"},"headline":"Getting started with Roslyn code analysis","datePublished":"2020-05-11T21:25:00+00:00","dateModified":"2021-09-18T13:35:24+00:00","mainEntityOfPage":{"@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/"},"wordCount":485,"image":{"@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.wiseowls.co.nz\/wp-content\/uploads\/2020\/03\/office-working-app-computer-97077_cr-scaled.jpg","keywords":["c#"],"articleSection":["Development"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/","url":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/","name":"Getting started with Roslyn code analysis - Timur and associates","isPartOf":{"@id":"https:\/\/blog.wiseowls.co.nz\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/#primaryimage"},"image":{"@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.wiseowls.co.nz\/wp-content\/uploads\/2020\/03\/office-working-app-computer-97077_cr-scaled.jpg","datePublished":"2020-05-11T21:25:00+00:00","dateModified":"2021-09-18T13:35:24+00:00","author":{"@id":"https:\/\/blog.wiseowls.co.nz\/#\/schema\/person\/34d0ed30d573b5bc317ea990bd2e0c59"},"description":"In this article we will build a quick test bench to walk C# code using Roslyn, open source compiler API from Microsoft.","breadcrumb":{"@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/#primaryimage","url":"https:\/\/blog.wiseowls.co.nz\/wp-content\/uploads\/2020\/03\/office-working-app-computer-97077_cr-scaled.jpg","contentUrl":"https:\/\/blog.wiseowls.co.nz\/wp-content\/uploads\/2020\/03\/office-working-app-computer-97077_cr-scaled.jpg","width":2560,"height":725},{"@type":"BreadcrumbList","@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/05\/12\/walking-code-with-roslyn\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.wiseowls.co.nz\/"},{"@type":"ListItem","position":2,"name":"Getting started with Roslyn code analysis"}]},{"@type":"WebSite","@id":"https:\/\/blog.wiseowls.co.nz\/#website","url":"https:\/\/blog.wiseowls.co.nz\/","name":"Timur and associates","description":"Notes of an IT contractor","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.wiseowls.co.nz\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blog.wiseowls.co.nz\/#\/schema\/person\/34d0ed30d573b5bc317ea990bd2e0c59","name":"timur","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg","url":"https:\/\/secure.gravatar.com\/avatar\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg","caption":"timur"},"sameAs":["https:\/\/x.com\/TimurKh"]}]}},"_links":{"self":[{"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/posts\/411","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/comments?post=411"}],"version-history":[{"count":16,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/posts\/411\/revisions"}],"predecessor-version":[{"id":963,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/posts\/411\/revisions\/963"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/media\/420"}],"wp:attachment":[{"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/media?parent=411"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/categories?post=411"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/tags?post=411"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}