{"id":29368,"date":"2020-08-27T11:00:02","date_gmt":"2020-08-27T18:00:02","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=29368"},"modified":"2021-09-29T12:15:49","modified_gmt":"2021-09-29T19:15:49","slug":"automatically-find-latent-bugs-in-your-code-with-net-5","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5\/","title":{"rendered":"Automatically find latent bugs in your code with .NET 5"},"content":{"rendered":"<div>\n<p>It&#8217;s an exciting time to be writing code! Especially for .NET developers as the platform keeps getting smarter. We now include rich diagnostics and code suggestions in the .NET SDK by default. Before you would need to install NuGet packages or other stand-alone tools to get more code analysis. Now, you will automatically get these in the new .NET 5 SDK.<\/p>\n<p>In the past, we&#8217;ve been reluctant to add new warnings to C#. This is because adding new warnings is technically a source breaking change for users who have warnings set as errors. However, there are a lot of cases we&#8217;ve come across over the years where we also really want to warn people that something was wrong, ranging from common coding mistakes to common API misuse patterns.<\/p>\n<p>Starting with .NET 5, we&#8217;re introducing what we&#8217;re calling <code>AnalysisLevel<\/code> in the C# compiler to introduce warnings for these patterns in a safe way. The default Analysis Level for all projects targeting .NET 5 will be set to 5, meaning that more warnings (and suggestions to fix them) will be introduced.<\/p>\n<p>Let&#8217;s talk about what the possible values for <code>AnalysisLevel<\/code> mean in your project. First thing we should note: unless you override the default, <code>AnalysisLevel<\/code> is set based on your target framework:<\/p>\n<div><\/div>\n<\/div>\n<table class=\" aligncenter\">\n<tbody>\n<tr>\n<td>Target Framework<\/td>\n<td>Default for <code>AnalysisLevel<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>net5.0<\/code><\/td>\n<td>5<\/td>\n<\/tr>\n<tr>\n<td><code>netcoreapp3.1<\/code>\u00a0or lower<\/td>\n<td>4<\/td>\n<\/tr>\n<tr>\n<td><code>netstandard2.1<\/code>\u00a0or lower<\/td>\n<td>4<\/td>\n<\/tr>\n<tr>\n<td><code>.NET Framework 4.8<\/code>\u00a0or lower<\/td>\n<td>4<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>However, what about the numbers 0-3? here is a more detailed breakdown of what each analysis level value means.<\/p>\n<table class=\" aligncenter\">\n<tbody>\n<tr>\n<td><code>AnalysisLevel<\/code><\/td>\n<td>Effect On C# Compiler<\/td>\n<td>Advanced Platform API Analysis<\/td>\n<\/tr>\n<tr>\n<td>5<\/td>\n<td>Get new compiler language analysis (details below)<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>identical to passing\u00a0<code>-warn:4<\/code>\u00a0to the C# compiler in previous versions<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>identical to passing\u00a0<code>-warn:3<\/code>\u00a0to the C# compiler in previous versions<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td>identical to passing\u00a0<code>-warn:2<\/code>\u00a0to the C# compiler in previous versions<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>identical to passing\u00a0<code>-warn:1<\/code>\u00a0to the C# compiler in previous versions<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>0<\/td>\n<td>identical to passing\u00a0<code>-warn:0<\/code>\u00a0to the C# compiler in previous versions, turns off all emission of warnings<\/td>\n<td>No<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>Since <code>AnalysisLevel<\/code> is tied to the target framework of your project, unless you change what your code targets, you will never change your default analysis level. You can manually set your analysis level though. For example, even if we are targeting .NET Core App 3.1 or .NET Standard (and therefore have <code>AnalysisLevel<\/code> defaulted to 4) you can still opt into a higher level.<\/p>\n<p>Here is an example of doing that:<\/p>\n<pre class=\"prettyprint\">&lt;Project\u00a0Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n\r\n\u00a0\u00a0&lt;PropertyGroup&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;OutputType&gt;Exe&lt;\/OutputType&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;TargetFramework&gt;netcoreapp3.1&lt;\/TargetFramework&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;!--\u00a0get\u00a0more\u00a0advanced\u00a0warnings\u00a0for\u00a0this\u00a0project\u00a0--&gt;\r\n\u00a0\u00a0\u00a0\u00a0&lt;AnalysisLevel&gt;5&lt;\/AnalysisLevel&gt;\r\n\u00a0\u00a0&lt;\/PropertyGroup&gt;\r\n\r\n&lt;\/Project&gt;<\/pre>\n<div>\n<div><span style=\"font-size: 1rem;\">If you always want to be on the highest supported analysis level you can specify <code>latest<\/code> in your project file:<\/span><\/div>\n<\/div>\n<pre class=\"prettyprint\">&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n\r\n  &lt;PropertyGroup&gt;\r\n    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\r\n    &lt;TargetFramework&gt;netcoreapp3.1&lt;\/TargetFramework&gt;\r\n    &lt;!-- be automatically updated to the newest stable level --&gt;\r\n    &lt;AnalysisLevel&gt;latest&lt;\/AnalysisLevel&gt;\r\n  &lt;\/PropertyGroup&gt;\r\n\r\n&lt;\/Project&gt;<\/pre>\n<p>If you are\u00a0<em>very\u00a0<\/em>adventurous and want to try out experimental compiler and platform analysis you can specify\u00a0<code>preview\u00a0<\/code>to get the latest, cutting-edge code diagnostics.<\/p>\n<p>Please note that <span class=\"pl-k\">when<\/span> you use <code>latest<\/code> <span class=\"pl-k\">or<\/span> <code>preview<\/code>, the analysis results might vary between machines, depending on the available SDK <span class=\"pl-k\">and<\/span> the highest analysis level it offers.<\/p>\n<pre class=\"prettyprint\">&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n\r\n  &lt;PropertyGroup&gt;\r\n    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\r\n    &lt;TargetFramework&gt;netcoreapp3.1&lt;\/TargetFramework&gt;\r\n    &lt;!-- be opted into experimental code correctness warnings --&gt;\r\n    &lt;AnalysisLevel&gt;preview&lt;\/AnalysisLevel&gt;\r\n  &lt;\/PropertyGroup&gt;\r\n\r\n&lt;\/Project&gt;<\/pre>\n<p>Finally, we have\u00a0<code>none\u00a0<\/code>which means\u00a0<em>&#8220;I don&#8217;t want to see any new warnings.&#8221;<\/em> <span class=\"x x-last\">In this mode, you won&#8217;t get any of the <\/span>advanced <span class=\"x x-first x-last\">API <\/span>analysis<span class=\"x x-first x-last\"> nor new compiler warnings.<\/span> This <span class=\"x x-first\">is useful <\/span><span class=\"pl-k x\">if<\/span> you <span class=\"x x-first\">need to update your framework but you&#8217;re <\/span><span class=\"pl-k x\">not<\/span><span class=\"x x-last\"> ready to absorb new warnings yet.<\/span><\/p>\n<pre class=\"prettyprint\">&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n\r\n  &lt;PropertyGroup&gt;\r\n    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\r\n    &lt;TargetFramework&gt;net5.0&lt;\/TargetFramework&gt;\r\n    &lt;!-- I am just fine thanks --&gt;\r\n    &lt;AnalysisLevel&gt;none&lt;\/AnalysisLevel&gt;\r\n  &lt;\/PropertyGroup&gt;\r\n\r\n&lt;\/Project&gt;<\/pre>\n<p>You can also configure the analysis level for a project from within Visual Studio via the Code Analysis property page. Just navigate to the project property page from the Solution Explorer. Then go to the Code Analysis tab.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-29490\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/configure-analysis-level-in-visual-studio-2.png\" alt=\"Configure Analysis Level in Visual Studio\" width=\"431\" height=\"280\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/configure-analysis-level-in-visual-studio-2.png 624w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/configure-analysis-level-in-visual-studio-2-300x195.png 300w\" sizes=\"(max-width: 431px) 100vw, 431px\" \/><\/p>\n<p>In the future we will add a new analysis level for every release of .NET. The goal is to make sure that a given analysis level always represents the same set of analysis defaults (the rules <span class=\"pl-k\">and<\/span> their severities). <span class=\"pl-k\">If<\/span> we want to turn an existing rule on by default, we&#8217;ll <span class=\"pl-k\">do<\/span> this in an upcoming analysis level, instead of changing the existing level. This ensures that a given project\/source always produces the same warning, regardless of how new the SDK is (<span class=\"pl-k\">unless<\/span> the project uses <code>preview<\/code> <span class=\"pl-k\">or<\/span> <code>latest<\/code>, of course).<\/p>\n<p><span style=\"font-size: 1rem;\">Since all .NET 5 projects will be opted into <\/span><em style=\"font-size: 1rem;\">Analysis Level 5<\/em><span style=\"font-size: 1rem;\">, let&#8217;s look at some of the new warnings and suggestions that will be offered.<\/span><\/p>\n<p>&nbsp;<\/p>\n<h2>All New Warnings and Errors coming in Analysis Level 5<\/h2>\n<p>The ones in\u00a0<strong>bold<\/strong>\u00a0are going to be in level 5 by the time .NET 5 ships. The rest are new warnings are available\u00a0today\u00a0in .NET 5 Preview 8 with Visual Studio 2019 16.8 Preview 2!<\/p>\n<table class=\" aligncenter\" style=\"height: 363px; width: 93.5056%; border-style: groove;\">\n<tbody>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\">Id<\/td>\n<td style=\"height: 26px; width: 9.34256%;\">Category<\/td>\n<td style=\"height: 26px; width: 5.36332%;\">Severity<\/td>\n<td style=\"height: 26px; width: 76.8717%;\">Description<\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#warn-when-code-does-not-work-across-all-platforms\"><strong><strong>CA1416<\/strong><\/strong><\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\"><strong>Interoperability<\/strong><\/td>\n<td style=\"height: 26px; width: 5.36332%;\"><strong>Warning<\/strong><\/td>\n<td style=\"height: 26px; width: 76.8717%;\"><strong>Warn when code does not work across all platforms<\/strong><\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#do-not-use-outattribute-on-string-parameters-for-pinvokes\">CA1417<\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\">Interoperability<\/td>\n<td style=\"height: 26px; width: 5.36332%;\">Warning<\/td>\n<td style=\"height: 26px; width: 76.8717%;\">Do not use <code>OutAttribute<\/code> on string parameters for P\/Invokes<\/td>\n<\/tr>\n<tr style=\"height: 51px;\">\n<td style=\"height: 51px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#use-asspan-instead-of-range-based-indexers-for-string-when-appropriate\">CA1831<\/a><\/td>\n<td style=\"height: 51px; width: 9.34256%;\">Performance<\/td>\n<td style=\"height: 51px; width: 5.36332%;\">Warning<\/td>\n<td style=\"height: 51px; width: 76.8717%;\">Use <code>AsSpan<\/code> instead of Range-based indexers for string when appropriate<\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#do-not-use-referenceequals-with-value-types\">CA2013<\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\">Reliability<\/td>\n<td style=\"height: 26px; width: 5.36332%;\">Warning<\/td>\n<td style=\"height: 26px; width: 76.8717%;\">Do not use <code>ReferenceEquals<\/code> with value types<\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#do-not-use-stackalloc-in-loops\">CA2014<\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\">Reliability<\/td>\n<td style=\"height: 26px; width: 5.36332%;\">Warning<\/td>\n<td style=\"height: 26px; width: 76.8717%;\">Do not use\u00a0<code>stackalloc\u00a0<\/code>in loops<\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#do-not-define-finalizers-for-types-derived-from-memorymanager\">CA2015<\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\">Reliability<\/td>\n<td style=\"height: 26px; width: 5.36332%;\">Warning<\/td>\n<td style=\"height: 26px; width: 76.8717%;\">Do not define finalizers for types derived from\u00a0<code>MemoryManager<\/code><\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#rethrow-to-preserve-stack-details\"><strong><strong>CA2200<\/strong><\/strong><\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\"><strong>Usage<\/strong><\/td>\n<td style=\"height: 26px; width: 5.36332%;\"><strong>Warning<\/strong><\/td>\n<td style=\"height: 26px; width: 76.8717%;\"><strong>Rethrow to preserve stack details<\/strong><\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#argument-passed-to-taskcompletionsource-calls-the-wrong-constructor\">CA2247<\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\">Usage<\/td>\n<td style=\"height: 26px; width: 5.36332%;\">Warning<\/td>\n<td style=\"height: 26px; width: 76.8717%;\">Argument passed to\u00a0<code>TaskCompletionSource<\/code>\u00a0calls the wrong constructor<\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#track-definite-assignment-of-structs-across-assemblies\">CS0177<\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\">Correctness<\/td>\n<td style=\"height: 26px; width: 5.36332%;\">Warning<\/td>\n<td style=\"height: 26px; width: 76.8717%;\">track definite assignment of structs across assemblies<\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#do-not-allow-locks-on-non-reference-types\"><strong><strong>CS0185<\/strong><\/strong><\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\"><strong>Correctness<\/strong><\/td>\n<td style=\"height: 26px; width: 5.36332%;\"><strong>Error<\/strong><\/td>\n<td style=\"height: 26px; width: 76.8717%;\"><strong>do not allow locks on non-reference types<\/strong><\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#do-not-allow-as-or-is-on-static-types\"><strong><strong>CS7023<\/strong><\/strong><\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\"><strong>Correctness<\/strong><\/td>\n<td style=\"height: 26px; width: 5.36332%;\"><strong>Error<\/strong><\/td>\n<td style=\"height: 26px; width: 76.8717%;\"><strong>do not allow\u00a0<code>as\u00a0<\/code>or\u00a0<code>is<\/code>\u00a0on static types<\/strong><\/td>\n<\/tr>\n<tr style=\"height: 26px;\">\n<td style=\"height: 26px; width: 5.27682%;\"><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/automatically-find-latent-bugs-in-your-code-with-net-5#warn-when-expression-is-always-true-or-false\">CS8073<\/a><\/td>\n<td style=\"height: 26px; width: 9.34256%;\">Usage<\/td>\n<td style=\"height: 26px; width: 5.36332%;\">Warning<\/td>\n<td style=\"height: 26px; width: 76.8717%;\">warn when expression is always false or true<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<h2>Warnings for common mistakes<\/h2>\n<p>The first set of new warnings are intended to find latent bugs, often in larger codebases. These can be very easy to introduce without additional compiler analysis today.<\/p>\n<h3 id=\"warn-when-expression-is-always-true-or-false\">Warn when expression is always true or false<\/h3>\n<p>This new warning is extremely common. Consider the following code:<\/p>\n<pre class=\"prettyprint\">public void M(DateTime dateTime)\r\n{\r\n    if (dateTime == null) \/\/ warning CS8073\r\n    {\r\n        return;\r\n    }\r\n}<\/pre>\n<p><code>DateTime<\/code> is a <code>struct<\/code> and <code>struct<\/code>s cannot be <code>null<\/code>. Starting in .NET 5 we will warn about this case with <code>CS8073<\/code>. The warning message is:<\/p>\n<p><em>Warning CS8073: The result of the expression is always &#8216;false&#8217; since the value of type &#8216;DateTime&#8217; is never equal to &#8216;null&#8217; of type &#8216;DateTime?&#8217;<\/em><\/p>\n<p>It might seem rather obvious what this code is doing is unnecessary in isolation but consider that such a check might occur in a method with 10 parameters to validate. To fix this you can remove the code (since its always false it&#8217;s not doing anything anyways), or change its type to <code>DateTime?<\/code> if <code>null<\/code> is an intended value for the parameter.<\/p>\n<pre class=\"prettyprint\">public void M(DateTime? dateTime) \/\/ We accept a null DateTime\r\n{\r\n    if (dateTime == null) \/\/ No Warnings\r\n    {\r\n        return;\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"do-not-allow-as-or-is-on-static-type\">Do not allow as or is on static types<\/h3>\n<p>This next one is a nice little enhancement:<\/p>\n<pre class=\"prettyprint\">static class Fiz\r\n{\r\n}\r\n\r\nclass P\r\n{\r\n    bool M(object o)\r\n    {\r\n        return o is Fiz; \/\/ CS7023\r\n    }\r\n}<\/pre>\n<p>Because <code>Fiz\u00a0<\/code>is a static class an instance object like\u00a0<code>o<\/code>\u00a0will never be able to be an instance of this type. We will get this warning:<\/p>\n<p class=\"prettyprint\"><em>Warning CS7023 The second operand of an &#8216;is&#8217; or &#8216;as&#8217; operator may not be static type &#8216;Fiz&#8217;<\/em><\/p>\n<p>The fix for this is to refactor our code (maybe we are actually checking against the wrong type to begin with), or to make the class\u00a0<code>Fiz\u00a0<\/code>non-static:<\/p>\n<pre class=\"prettyprint\">class Fiz\r\n{\r\n}\r\n\r\nclass P\r\n{\r\n    bool M(object o)\r\n    {\r\n        return o is Fiz; \/\/ no error\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"do-not-allow-locks-on-non-reference-types\">Do not allow locks on non-reference types<\/h3>\n<p>locking on a non-reference type (like an\u00a0<code>int<\/code>) does nothing because they are pass-by-value so a different version of them lives on every stack frame. In the past we would warn you about locking on non-reference types for simple cases like\u00a0<code>lock(5)<\/code>\u00a0but until recently we would not warn you for open generics like below.<\/p>\n<pre class=\"prettyprint\">public class P\r\n{\r\n    public static void GetValue&lt;TKey&gt;(TKey key)\r\n    {\r\n        lock (key) \/\/ CS0185\r\n        {\r\n        }\r\n    }\r\n\r\n    static void Main()\r\n    {\r\n        GetValue(1);\r\n    }\r\n}<\/pre>\n<p>This is an error because passing in an int (which is allowed under this unconstrained generic) will not actually lock correctly. We&#8217;ll see this error:<\/p>\n<p class=\"prettyprint\"><em>Error\u00a0CS0185\u00a0&#8216;TKey&#8217;\u00a0is\u00a0not\u00a0a\u00a0reference\u00a0type\u00a0as\u00a0required\u00a0by\u00a0the\u00a0lock\u00a0statement<\/em><\/p>\n<p>To fix this we need to indicate that the\u00a0<code>GetValue\u00a0<\/code>method should only be given reference types. We can do this with the generic type constraint\u00a0<code>where TKey : class<\/code><\/p>\n<pre class=\"prettyprint\">public class P\r\n{\r\n    public static void GetValue&lt;TKey&gt;(TKey key) where TKey : class\r\n    {\r\n        lock (key) \/\/ no error\r\n        {\r\n        }\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"rethrow-to-preserve-stack-details\">Rethrow to preserve stack details<\/h3>\n<p>We&#8217;re all good (?) developers so our code never throws exceptions, right? Well even the best developers need to handle exceptions in .NET and one of the common pitfalls new programmers fall into is this:<\/p>\n<pre class=\"prettyprint\">try\r\n{\r\n    throw new Exception();\r\n}\r\ncatch (Exception ex)\r\n{\r\n    \/\/ probably logging some info here...\r\n\r\n    \/\/ rethrow now that we are done\r\n    throw ex; \/\/ CA2200\r\n}<\/pre>\n<p>In school I learned that if someone threw the ball at me and I caught it, I had to throw the ball back! Metaphors like this lead lots of folks to believe that\u00a0<code>throw ex<\/code>\u00a0is the correct way to re-throw this exception. Sadly, this will change the stacks in the original exception. Now you will get a warning that this is happening. It looks like this:<\/p>\n<p class=\"prettyprint\"><em>Warning\u00a0CA2200\u00a0Re-throwing\u00a0caught\u00a0exception\u00a0changes\u00a0stack\u00a0information<\/em><\/p>\n<p>In nearly all cases the correct thing to do here is to simply use the\u00a0<code>throw\u00a0<\/code>keyword without mentioning the variable of the exception we caught.<\/p>\n<pre class=\"prettyprint\">try\r\n{\r\n    throw new Exception();\r\n}\r\ncatch (Exception ex)\r\n{\r\n    \/\/ probably logging some info here...\r\n\r\n    \/\/ rethrow now that we are done\r\n    throw;\r\n}<\/pre>\n<p>We also offer a code fix to easily fix up all of these at once in your document, project, or solution!<\/p>\n<p><img decoding=\"async\" class=\"wp-image-29491\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/code-fix-for-rethrow-2.png\" alt=\"Code fix for Rethrow\" width=\"444\" height=\"180\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/code-fix-for-rethrow-2.png 624w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/code-fix-for-rethrow-2-300x122.png 300w\" sizes=\"(max-width: 444px) 100vw, 444px\" \/><\/p>\n<p>&nbsp;<\/p>\n<h3 id=\"do-not-use-referenceequals-with-value-types\">Do not use ReferenceEquals with value types<\/h3>\n<p>Equality is a tricky topic in .NET. This next warning strives to make accidentally comparing a <code>struct\u00a0<\/code>by reference apparent. Consider the code below:<\/p>\n<pre class=\"prettyprint\">int int1 = 1;\r\nint int2 = 1;\r\nConsole.WriteLine(object.ReferenceEquals(int1, int2)); \/\/ warning CA2013<\/pre>\n<p>This will box the two <code>int<\/code>s and\u00a0<code>ReferenceEquals\u00a0<\/code>will always return false as a result. We will see this warning description:<\/p>\n<p class=\"prettyprint\"><em>Warning CA2013: Do not pass an argument with value type &#8216;int&#8217; to &#8216;ReferenceEquals&#8217;. Due to value boxing, this call to &#8216;ReferenceEquals&#8217; will always return &#8216;false&#8217;.<\/em><\/p>\n<p>The fix for this error is to either use the equality operator\u00a0<code>==<\/code>\u00a0or\u00a0<code>object.Equals<\/code>\u00a0like so:<\/p>\n<pre class=\"prettyprint\">int int1 = 1;\r\nint int2 = 1;\r\nConsole.WriteLine(int1 == int2); \/\/ using the equality operator is fine\r\nConsole.WriteLine(object.Equals(int1, int2));  \/\/ so is object.Equals<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"track-definite-assignment-of-structs-across-assemblies\">Track definite assignment of structs across assemblies<\/h3>\n<p>This next warning is something that a lot of people may be surprised to learn wasn&#8217;t already a warning:<\/p>\n<pre class=\"prettyprint\">using System.Collections.Immutable;\r\n\r\nclass P\r\n{\r\n    public void M(out ImmutableArray&lt;int&gt; immutableArray) \/\/ CS0177\r\n    {\r\n    }\r\n}<\/pre>\n<p>This rule is all about <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/language-reference\/language-specification\/variables#definite-assignment\">definite assignment<\/a>,\u00a0a useful feature in C# that makes sure you don&#8217;t forget to assign values to your variables.<\/p>\n<p class=\"prettyprint\"><em>Warning CS0177: The out parameter &#8216;immutableArray&#8217; must be assigned to before control leaves the current method<\/em><\/p>\n<p><code>CS0177\u00a0<\/code>is already issued for several different situations today, but not in the case previously shown. The history here is that this was a bug that traces itself all the way back to the original implementations of the C# compiler. Previously, the C# compiler ignored private fields of reference types in a value type imported from metadata when computing definite assignment. This <i>extremely <\/i>specific\u00a0bug meant that a type like\u00a0<code>ImmutableArray\u00a0<\/code>was able to escape definite assignment analysis. Ouch!<\/p>\n<p>Now the compiler will correctly error for you and you can fix it by simply ensuring that it is always assigned a value, like so:<\/p>\n<pre class=\"prettyprint\">using System.Collections.Immutable;\r\n\r\nclass P\r\n{\r\n    public bool M(out ImmutableArray&lt;int&gt; immutableArray) \/\/ no warning\r\n    {\r\n        immutableArray = ImmutableArray&lt;int&gt;.Empty;\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h2>Warning for incorrect .NET API usage<\/h2>\n<p>The next examples are about correctly using .NET libraries. Analysis Levels allow for guarding against improper use of existing .NET APIs today, but it also has an impact on .NET library evolution moving forward. If a useful API is designed but it has the potential for misuse, a new warning that detects misuse can also be added in tandem with the new API.<\/p>\n<p>&nbsp;<\/p>\n<h3 id=\"do-not-define-finalizers-for-types-derived-from-memorymanager\">Do not define finalizers for types derived from MemoryManager<\/h3>\n<p><code>MemoryManager<\/code> is a useful class for when you want to implement your own <code>Memory&lt;T&gt;<\/code> type. This is not something you&#8217;re likely to find yourself doing a lot, but when you need it you <em>really <\/em>need it. This new warning triggers for cases like this:<\/p>\n<pre class=\"prettyprint\">class DerivedClass &lt;T&gt; : MemoryManager&lt;T&gt;\r\n{\r\n    public override bool Dispose(bool disposing)\r\n    {\r\n        if (disposing)\r\n        {\r\n            _handle.Dispose();\r\n        }\r\n    }\r\n  \r\n    ~DerivedClass() =&gt; Dispose(false); \/\/ warning CA2015\r\n}<\/pre>\n<p>Adding a finalizer to this type can introduce holes in the garbage collector, which we all would prefer to avoid!<\/p>\n<p class=\"prettyprint\"><em>Warning CA2015 Adding a finalizer to a type derived from MemoryManager&lt;T&gt; may permit memory to be freed while it is still in use by a Span&lt;T&gt;.<\/em><\/p>\n<p>The fix is to remove this finalizer, since it will cause very subtle bugs in your program that will be hard to find and fix.<\/p>\n<pre class=\"prettyprint\">class DerivedClass &lt;T&gt; : MemoryManager&lt;T&gt;\r\n{\r\n    public override bool Dispose(bool disposing)\r\n    {\r\n        if (disposing)\r\n        {\r\n            _handle.Dispose();\r\n        }\r\n    }\r\n \/\/ No warning, since there is no finalizer here\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"argument-passed-to-taskcompletionsource-calls-the-wrong-constructor\">Argument passed to TaskCompletionSource calls the wrong constructor<\/h3>\n<p>This warning notifies us that we&#8217;ve used just\u00a0slightly\u00a0the wrong enum.<\/p>\n<pre class=\"prettyprint\">var\u00a0tcs\u00a0=\u00a0new\u00a0TaskCompletionSource(TaskContinuationOptions.RunContinuationsAsynchronously);\u00a0\/\/\u00a0warning\u00a0CA2247<\/pre>\n<p>Unless you are already aware of the issue you may stare at this for a bit before you see it. The problem is that this constructor does not take a\u00a0<code>TaskContinuationOptions\u00a0<\/code>enum it takes a\u00a0<code>TaskCreationOptions\u00a0<\/code>enum. What is happening is that we are calling the constructor for\u00a0<code>TaskCompletionSource\u00a0<\/code>that accepts\u00a0<code>object<\/code>! Considering how similar their names are and that they have very similar values this mistake is easy to make.<\/p>\n<p class=\"prettyprint\"><em>Warning\u00a0CA2247:\u00a0Argument\u00a0contains\u00a0TaskContinuationsOptions\u00a0enum\u00a0instead\u00a0of TaskCreationOptions\u00a0enum.<\/em><\/p>\n<p>The fix is to pass in the correct enum type:<\/p>\n<pre class=\"prettyprint\">var\u00a0tcs\u00a0=\u00a0new\u00a0TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);\u00a0\/\/\u00a0no\u00a0warning<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"warn-when-code-does-not-work-across-all-platforms\">Warn when code does not work across all platforms<\/h3>\n<p>This last one is a doozy! I won&#8217;t go into all its intricacies here (look forward to a future blog post on that topic). But the purpose of warnings here is to let you know that the APIs you are calling may not work on all the targets you are building for.<\/p>\n<p>Let&#8217;s say I have an app that runs on both Linux and Windows. I have a method that I use to get the path to create log files under and it has different behavior based on where it is running.<\/p>\n<pre class=\"prettyprint\">private static string GetLoggingPath()\r\n{\r\n    var appDataDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\r\n    var loggingDirectory = Path.Combine(appDataDirectory, \"Fabrikam\", \"AssetManagement\", \"Logging\");\r\n\r\n    \/\/ Create the directory and restrict access using Windows\r\n    \/\/ Access Control Lists (ACLs).\r\n\r\n    var rules = new DirectorySecurity(); \/\/ CA1416\r\n    rules.AddAccessRule(\r\n        new FileSystemAccessRule(@\"fabrikam\\log-readers\",\r\n                                    FileSystemRights.Read,\r\n                                    AccessControlType.Allow)\r\n    );\r\n    rules.AddAccessRule(\r\n        new FileSystemAccessRule(@\"fabrikam\\log-writers\",\r\n                                    FileSystemRights.FullControl,\r\n                                    AccessControlType.Allow)\r\n    );\r\n\r\n    if (!OperatingSystem.IsWindows())\r\n    {\r\n        \/\/ Just create the directory\r\n        Directory.CreateDirectory(loggingDirectory);\r\n    }\r\n    else\r\n    {\r\n        Directory.CreateDirectory(loggingDirectory, rules);\r\n    }\r\n\r\n    return loggingDirectory;\r\n}<\/pre>\n<p>I correctly use the\u00a0<a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.operatingsystem\">OperatingSystem<\/a> helper to check if the OS is windows with <code>OperatingSystem.IsWindows()<\/code> and only pass the rules for that case, but I actually have already used platform specific APIs that will not work on Linux!<\/p>\n<p class=\"prettyprint\"><em>Warning\u00a0CA1416:\u00a0&#8216;DirectorySecurity&#8217;\u00a0is\u00a0unsupported\u00a0on\u00a0&#8216;Linux&#8217;<\/em><\/p>\n<p>The correct way to handle this is to move all my platform specific code inside the\u00a0else\u00a0statement.<\/p>\n<pre class=\"prettyprint\">private static string GetLoggingPath()\r\n{\r\n    var appDataDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);\r\n    var loggingDirectory = Path.Combine(appDataDirectory, \"Fabrikam\", \"AssetManagement\", \"Logging\");\r\n\r\n    if (!OperatingSystem.IsWindows())\r\n    {\r\n        \/\/ Just create the directory\r\n        Directory.CreateDirectory(loggingDirectory);\r\n    }\r\n    else\r\n    {\r\n        \/\/ Create the directory and restrict access using Windows\r\n        \/\/ Access Control Lists (ACLs).\r\n\r\n        var rules = new DirectorySecurity(); \/\/ no warning\r\n        rules.AddAccessRule(\r\n            new FileSystemAccessRule(@\"fabrikam\\log-readers\",\r\n                                        FileSystemRights.Read,\r\n                                        AccessControlType.Allow)\r\n        );\r\n        rules.AddAccessRule(\r\n            new FileSystemAccessRule(@\"fabrikam\\log-writers\",\r\n                                        FileSystemRights.FullControl,\r\n                                        AccessControlType.Allow)\r\n        );\r\n\r\n        Directory.CreateDirectory(loggingDirectory, rules);\r\n    }\r\n\r\n    return loggingDirectory;\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h2>Low level coding help<\/h2>\n<p>There are also a few warnings that are useful when writing high-performance applications. These next set of warnings ensure you don\u2019t need to sacrifice safety for these cases.<\/p>\n<p>&nbsp;<\/p>\n<h3 id=\"do-not-use-outattribute-on-string-parameters-for-pinvokes\">Do not use OutAttribute on string parameters for P\/Invokes<\/h3>\n<p>Sometimes you need to interoperate with native code. .NET has the concept of platform invocations\u00a0(<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/standard\/native-interop\/pinvoke\">P\/Invokes<\/a>) to make this process easier. However, there are a few gotchas in terms of sending data to and from native libraries in .NET. Consider the code below:<\/p>\n<pre class=\"prettyprint\">[DllImport(\"MyLibrary\")]\r\nprivate static extern void Goo([Out] string s); \/\/ warning CA1417<\/pre>\n<p>Unless you are very familiar with writing P\/Invokes, it&#8217;s not obvious what is wrong here. You normally apply\u00a0<code>OutAttribute\u00a0<\/code>to types that the runtime doesn&#8217;t know about to indicate how the type should be marshaled. The OutAttribute implies that you are passing the data by value. It doesn&#8217;t make sense for strings to be passed by value though and has the potential to crash the runtime.<\/p>\n<p class=\"prettyprint\"><em>Warning CA1417 Do not use the &#8216;OutAttribute&#8217; for string parameter &#8216;s&#8217; which is passed by value. If marshalling of modified data back to the caller is required, use the &#8216;out&#8217; keyword to pass the string by reference instead.<\/em><\/p>\n<p>The fix for this is to either treat it as a normal out parameter (passing by reference).<\/p>\n<pre class=\"prettyprint\">[DllImport(\"MyLibrary\")]\r\nprivate static extern void Goo(out string s); \/\/ no warning<\/pre>\n<p>or if you don\u2019t need the string marshaled back to the caller you can just do this:<\/p>\n<pre class=\"prettyprint\">[DllImport(\"MyLibrary\")]\r\nprivate static extern void Goo(string s); \/\/ no warning<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"use-asspan-instead-of-range-based-indexers-for-string-when-appropriate\">Use AsSpan instead of Range-based indexers for string when appropriate<\/h3>\n<p>This is all about making sure that you don\u2019t accidentally allocate a string.<\/p>\n<pre class=\"prettyprint\">class Program\r\n{\r\n    public void TestMethod(string str)\r\n    {\r\n        ReadOnlySpan&lt;char&gt; slice = str[1..3]; \/\/ CA1831\r\n    }\r\n}<\/pre>\n<p>In the code above its clear the developers intent is to index a string using the new <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/proposals\/csharp-8.0\/ranges\">range-based index<\/a> feature in C#. Unfortunately, this will actually allocate a string unless you convert that string to a span first.<\/p>\n<p class=\"prettyprint\"><em>Warning CA1831 Use &#8216;AsSpan&#8217; instead of the &#8216;System.Range&#8217;-based indexer on &#8216;string&#8217; to avoid creating unnecessary data copies<\/em><\/p>\n<p>The fix is to just add AsSpan calls in this case:<\/p>\n<pre class=\"prettyprint\">class Program\r\n{\r\n    public void TestMethod(string str)\r\n    {\r\n        ReadOnlySpan&lt;char&gt; slice = str.AsSpan()[1..3]; \/\/ no warning\r\n    }\r\n}<\/pre>\n<h3 id=\"do-not-use-stackalloc-in-loops\">Do not use stackalloc in loops<\/h3>\n<p>The <code><a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/operators\/stackalloc\">stackalloc<\/a> <\/code>keyword is great for when you want to make sure the operations you are doing are easy on the garbage collector. In the past,\u00a0<code>stackalloc\u00a0<\/code>was only allowed in unsafe code, but since C# 8 it&#8217;s also been allowed outside of\u00a0<code>unsafe<\/code> blocks so long as that variable is assigned to a <code>Span&lt;T&gt;<\/code> or a <code>ReadOnlySpan&lt;T&gt;<\/code>.<\/p>\n<pre class=\"prettyprint\">class C\r\n{\r\n    public void TestMethod(string str)\r\n    {\r\n        int length = 3;\r\n        for (int i = 0; i &lt; length; i++)\r\n        {\r\n            Span&lt;int&gt; numbers = stackalloc int[length]; \/\/ CA2014\r\n            numbers[i] = i;\r\n        }\r\n    }\r\n}<\/pre>\n<p>Allocating a lot on the stack can lead to the famous StackOverflow exception, where we&#8217;ve allocated more memory on the stack than allowed. Allocating in a loop is especially perilous.<\/p>\n<p class=\"prettyprint\"><em>Warning CA2014 Potential stack overflow. Move the stackalloc out of the loop.<\/em><\/p>\n<p>The fix is to move our\u00a0<code>stackalloc\u00a0<\/code>out of the loop.<\/p>\n<pre class=\"prettyprint\">class C\r\n{\r\n    public void TestMethod(string str)\r\n    {\r\n        int length = 3;\r\n        Span&lt;int&gt; numbers = stackalloc int[length]; \/\/ no warning\r\n        for (int i = 0; i &lt; length; i++)\r\n        {\r\n            numbers[i] = i;\r\n        }\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<h2>Configuring Analysis Levels<\/h2>\n<p>Now that you&#8217;ve seen how useful these warnings are, you probably never want to go back to a world without them right? Well, I know that the world doesn&#8217;t always work that way. As mentioned at the beginning of this post, these are source breaking changes, and you should be able to take them on in a schedule that works for you. Part of the reason we&#8217;re introducing this now is to get feedback in two areas:<\/p>\n<ol>\n<li>If the small set of warnings we&#8217;re introducing is too disruptive or not<\/li>\n<li>If the mechanism for tuning the warnings is sufficient for your needs<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<h3>Going back to the .NET Core 3.1 analysis level:<\/h3>\n<p>If you just want to go back to the way things were before .NET 5 (meaning the warnings you got in .NET Core 3.1) all you need to do is set the analysis level to 4 in your project file. Here is an example:<\/p>\n<pre class=\"prettyprint\">&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n\r\n  &lt;PropertyGroup&gt;\r\n    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\r\n    &lt;TargetFramework&gt;net5.0&lt;\/TargetFramework&gt;\r\n    &lt;!-- get the exact same warnings you used to --&gt;\r\n    &lt;AnalysisLevel&gt;4&lt;\/AnalysisLevel&gt;\r\n  &lt;\/PropertyGroup&gt;\r\n\r\n&lt;\/Project&gt;<\/pre>\n<p>&nbsp;<\/p>\n<h3>Turning off just a single rule<\/h3>\n<p>If there is a specific warning that you believe is not applicable to your codebase you can use an <a href=\"https:\/\/docs.microsoft.com\/visualstudio\/ide\/create-portable-custom-editor-options\">editorconfig file<\/a> to turn it off. You can do this by either setting the severity of the warning to \u2018none\u2019 from the error list.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-29492\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-15.png\" alt=\"Set Severity From Error List\" width=\"493\" height=\"319\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-15.png 609w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-15-300x194.png 300w\" sizes=\"(max-width: 493px) 100vw, 493px\" \/><\/p>\n<p>Or by selecting \u201cNone\u201d from the lightbulb menu where the warning appears in the editor<\/p>\n<p><img decoding=\"async\" class=\"wp-image-29493\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-16.png\" alt=\"Set Severity From Lightbulb\" width=\"806\" height=\"199\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-16.png 996w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-16-300x74.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-16-768x190.png 768w\" sizes=\"(max-width: 806px) 100vw, 806px\" \/><\/p>\n<p>&nbsp;<\/p>\n<h3>Turning off a single instance of a warning<\/h3>\n<p>If you want a warning to be on almost all the time and only suppress it in a few instances you can use the lightbulb menu to either:<\/p>\n<ul>\n<li>Suppress it in source.<\/li>\n<\/ul>\n<p><img decoding=\"async\" class=\"wp-image-29494\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-17.png\" alt=\"Suppress in Source\" width=\"810\" height=\"198\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-17.png 1105w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-17-300x73.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-17-1024x250.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-17-768x188.png 768w\" sizes=\"(max-width: 810px) 100vw, 810px\" \/><\/p>\n<ul>\n<li>Suppress it in a separate suppression file.<\/li>\n<\/ul>\n<p><img decoding=\"async\" class=\"wp-image-29495\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-18.png\" alt=\"Suppress in Suppression File\" width=\"828\" height=\"225\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-18.png 1052w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-18-300x82.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-18-1024x278.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-18-768x209.png 768w\" sizes=\"(max-width: 828px) 100vw, 828px\" \/><\/p>\n<ul>\n<li>Suppress it in source with an attribute.<\/li>\n<\/ul>\n<p><img decoding=\"async\" class=\"wp-image-29496\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-19.png\" alt=\"Suppress in Attribute\" width=\"835\" height=\"190\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-19.png 1063w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-19-300x68.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-19-1024x233.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/08\/a-screenshot-of-a-social-media-post-description-a-19-768x175.png 768w\" sizes=\"(max-width: 835px) 100vw, 835px\" \/><\/p>\n<h2>Summary<\/h2>\n<p>I hope this has gotten you excited for all the improvements to code analysis that you can expect in .NET 5 and please give us feedback about this experience.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction to the new Analysis Level feature shipping in .NET 5 Preview 8<\/p>\n","protected":false},"author":8375,"featured_media":29490,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"quote","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,196,756,3009],"tags":[4],"class_list":["post-29368","post","type-post","status-publish","format-quote","has-post-thumbnail","hentry","category-dotnet","category-dotnet-core","category-csharp","category-performance","tag-net","post_format-post-format-quote"],"acf":[],"blog_post_summary":"<p>Introduction to the new Analysis Level feature shipping in .NET 5 Preview 8<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/29368","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/8375"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=29368"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/29368\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/29490"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=29368"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=29368"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=29368"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}