{"id":390689,"date":"2024-06-29T09:23:26","date_gmt":"2024-06-29T09:23:26","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=390689"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=390689","title":{"rendered":"<span>Errors and suspicious code fragments in .NET 6 sources<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/7a6\/522\/913\/7a6522913a700269aad35cfc06f0caa5.png\" alt=\"0903_NET6\/image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/7a6\/522\/913\/7a6522913a700269aad35cfc06f0caa5.png\"\/><\/p>\n<p>  <\/p>\n<p>The .NET 6 turned out to be much-awaited and major release. If you write for .NET, you could hardly miss such an event. We also couldn&#8217;t pass by the new version of this platform. We decided to check what interesting things we can find in the sources of .NET libraries.<\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h2 id=\"details-about-the-check\">Details about the check<\/h2>\n<p>  <\/p>\n<p>I took the sources from the branch of .NET 6 release <a href=\"https:\/\/github.com\/dotnet\/runtime\/tree\/v6.0.0\">on GitHub<\/a>. This article covers suspicious places only from the libraries (those that lies in src\/libraries). I didn&#8217;t analyze the runtime itself \u2014 maybe next time. \ud83d\ude42<\/p>\n<p>  <\/p>\n<p>I checked the code with the <a href=\"https:\/\/pvs-studio.com\/en\/pvs-studio\/\">PVS-Studio static analyzer<\/a>. As you probably guessed from this article, PVS-Studio 7.16 supports the analysis of projects on .NET 6. You can read more about new enhancements of the current release here. The PVS-Studio C# analyzer for Linux and macOS now works on .NET 6 as well.<\/p>\n<p>  <\/p>\n<p>Over the year, PVS-Studio significantly expanded the functionality of the C# analyzer. In addition to the support of the .NET 6 platform, we added the plugin for Visual Studio 2022 and new security-diagnostics. Besides, we also optimized the C# analyzer&#8217;s performance for large projects.<\/p>\n<p>  <\/p>\n<p>But you came here to read about .NET 6, didn&#8217;t you? Let&#8217;s not waste time.<\/p>\n<p>  <\/p>\n<h2 id=\"suspicious-code-fragments\">Suspicious code fragments<\/h2>\n<p>  <\/p>\n<h3 id=\"miscellaneous\">Miscellaneous<\/h3>\n<p>  <\/p>\n<p>This section includes various interesting code fragments that I could not group together into common category.<\/p>\n<p>  <\/p>\n<p><strong>Issue 1<\/strong><\/p>\n<p>  <\/p>\n<p>Let&#8217;s start with something simple.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public enum CompressionLevel {   Optimal,   Fastest,   NoCompression,   SmallestSize }  internal static void GetZipCompressionMethodFromOpcCompressionOption(   CompressionOption compressionOption,   out CompressionLevel compressionLevel) {   switch (compressionOption)   {     case CompressionOption.NotCompressed:       {         compressionLevel = CompressionLevel.NoCompression;       }       break;     case CompressionOption.Normal:       {         compressionLevel = CompressionLevel.Optimal;  \/\/ &lt;=       }       break;     case CompressionOption.Maximum:       {         compressionLevel = CompressionLevel.Optimal;  \/\/ &lt;=       }       break;     case CompressionOption.Fast:       {         compressionLevel = CompressionLevel.Fastest;       }       break;     case CompressionOption.SuperFast:       {         compressionLevel = CompressionLevel.Fastest;       }       break;      \/\/ fall-through is not allowed     default:       {         Debug.Fail(\"Encountered an invalid CompressionOption enum value\");         goto case CompressionOption.NotCompressed;       }   } }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3139\/\">V3139<\/a> Two or more case-branches perform the same actions. ZipPackage.cs 402<\/p>\n<p>  <\/p>\n<p>In fact, this method performs mapping from <em>CompressionOption<\/em> to <em>CompressionLevel<\/em>. The suspicious thing here is that the <em>CompressionOption.Normal<\/em> and <em>CompressionOption.Maximum<\/em> values are mapped to the <em>CompressionLevel.Optimal<\/em> value. <\/p>\n<p>  <\/p>\n<p>Probably<em> CompressionOption.Maximum <\/em>should match<em> CompressionLevel.SmallestSize<\/em>.<\/p>\n<p>  <\/p>\n<p><strong>Issue 2<\/strong><\/p>\n<p>  <\/p>\n<p>Now let&#8217;s practice a little. Let&#8217;s take the <em>System.Text.Json.Nodes.JsonObject<\/em> for our experiments. If you wish, you can repeat the described operations using the release version of .NET 6 SDK.<\/p>\n<p>  <\/p>\n<p>The <em>JsonObject<\/em> type has 2 constructors: one constructor accepts only options, the other \u2014 properties and options. Well, it&#8217;s clear what kind of behavior we should expect from them. Documentation is available <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.text.json.nodes.jsonobject.-ctor?view=net-6.0\">here<\/a>.<\/p>\n<p>  <\/p>\n<p>Let&#8217;s create two instances of the <em>JsonObject<\/em> type and use each of the constructors.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">static void JsonObject_Test() {   var properties = new Dictionary&lt;String, JsonNode?>();   var options = new JsonNodeOptions()   {     PropertyNameCaseInsensitive = true   };    var jsonObject1 = new JsonObject(options);   var jsonObject2 = new JsonObject(properties, options); }<\/code><\/pre>\n<p>  <\/p>\n<p>Now let&#8217;s check the state of the objects we created.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/dbd\/45e\/57c\/dbd45e57c0434814ce3f92b9b646484f.png\" alt=\"0903_NET6\/image2.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/dbd\/45e\/57c\/dbd45e57c0434814ce3f92b9b646484f.png\"\/><\/p>\n<p>  <\/p>\n<p>The <em>jsonObject1<\/em> state is expected, but the <em>jsonObject2<\/em> object state is not. Why the <em>null<\/em> value is written in the <em>_options<\/em> field? It&#8217;s a little confusing. Well, let&#8217;s open the source code and look at these constructors.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public sealed partial class JsonObject : JsonNode {   ....   public JsonObject(JsonNodeOptions? options = null) : base(options) { }    public JsonObject(IEnumerable&lt;KeyValuePair&lt;string, JsonNode?>> properties,                      JsonNodeOptions? options = null)   {     foreach (KeyValuePair&lt;string, JsonNode?> node in properties)     {       Add(node.Key, node.Value);     }   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>In the second constructor, the <em>options<\/em> parameter is simply abandoned \u2014 it is not passed anywhere and is not used in any way. Whereas in the first constructor, <em>options<\/em> are passed to the base class constructor, where they are written to the field:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">internal JsonNode(JsonNodeOptions? options = null) {   _options = options; }<\/code><\/pre>\n<p>  <\/p>\n<p>The corresponding PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3117\/\">V3117<\/a> Constructor parameter &#8216;options&#8217; is not used. JsonObject.cs 35<\/p>\n<p>  <\/p>\n<p><strong>Issue 3<\/strong><\/p>\n<p>  <\/p>\n<p>If we talk about the forgotten parameters, there was another interesting fragment.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public class ServiceNameCollection : ReadOnlyCollectionBase {   ....   private ServiceNameCollection(IList list, string serviceName)     : this(list, additionalCapacity: 1)   { .... }    private ServiceNameCollection(IList list, IEnumerable serviceNames)     : this(list, additionalCapacity: GetCountOrOne(serviceNames))   { .... }    private ServiceNameCollection(IList list, int additionalCapacity)   {     Debug.Assert(list != null);     Debug.Assert(additionalCapacity >= 0);      foreach (string? item in list)     {       InnerList.Add(item);     }   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3117\/\">V3117<\/a> Constructor parameter &#8216;additionalCapacity&#8217; is not used. ServiceNameCollection.cs 46<\/p>\n<p>  <\/p>\n<p>According to code, the <em>additionalCapacity<\/em> parameter of the last constructor is checked in <em>Debug.Assert<\/em> and not used for anything else. It looks suspicious. It&#8217;s especially amusing \u2014 other constructors pass some values for <em>additionalCapacity<\/em> parameter.<\/p>\n<p>  <\/p>\n<p><strong>Issue 4<\/strong><\/p>\n<p>  <\/p>\n<p>Here&#8217;s the test for the ability of foresight (oops, spoilers). Study the following code and try to guess what triggered the analyzer.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public override void CheckErrors() {   throw new XsltException(SR.Xslt_InvalidXPath,                            new string[] { Expression },                            _baseUri,                            _linePosition,                            _lineNumber,                            null); }<\/code><\/pre>\n<p>  <\/p>\n<p>It would seem that an exception is simply thrown. To understand what is wrong here, you need to look at the <em>XsltException<\/em> constructor.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">internal XsltException(string res,                         string?[] args,                         string? sourceUri,                         int lineNumber,                         int linePosition,                         Exception? inner) : base(....) { .... }<\/code><\/pre>\n<p>  <\/p>\n<p>If you compare the order of arguments and parameters, it becomes clear what triggered the analyzer. It looks like the line position and the line number switched places.<\/p>\n<p>  <\/p>\n<p>Order of arguments:<\/p>\n<p>  <\/p>\n<ul>\n<li><em>_linePosition<\/em><\/li>\n<li><em>_lineNumber<\/em><\/li>\n<\/ul>\n<p>  <\/p>\n<p>Order of parameters:<\/p>\n<p>  <\/p>\n<ul>\n<li><em>lineNumber<\/em><\/li>\n<li><em>linePosition<\/em><\/li>\n<\/ul>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3066\/\">V3066<\/a> Possible incorrect order of arguments passed to &#8216;XsltException&#8217; constructor: &#8216;_linePosition&#8217; and &#8216;_lineNumber&#8217;. Compiler.cs 1187<\/p>\n<p>  <\/p>\n<p><strong>Issue 5<\/strong><\/p>\n<p>  <\/p>\n<p>Here is sufficiently large piece of code. There must be some kind of typo hidden there\u2026 Would you like to try to find it?<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public Parser(Compilation compilation,                in JsonSourceGenerationContext sourceGenerationContext) {   _compilation = compilation;   _sourceGenerationContext = sourceGenerationContext;   _metadataLoadContext = new MetadataLoadContextInternal(_compilation);    _ilistOfTType = _metadataLoadContext.Resolve(     SpecialType.System_Collections_Generic_IList_T);   _icollectionOfTType = _metadataLoadContext.Resolve(     SpecialType.System_Collections_Generic_ICollection_T);   _ienumerableOfTType = _metadataLoadContext.Resolve(     SpecialType.System_Collections_Generic_IEnumerable_T);   _ienumerableType = _metadataLoadContext.Resolve(     SpecialType.System_Collections_IEnumerable);    _listOfTType = _metadataLoadContext.Resolve(typeof(List&lt;>));   _dictionaryType = _metadataLoadContext.Resolve(typeof(Dictionary&lt;,>));   _idictionaryOfTKeyTValueType = _metadataLoadContext.Resolve(     typeof(IDictionary&lt;,>));   _ireadonlyDictionaryType = _metadataLoadContext.Resolve(     typeof(IReadOnlyDictionary&lt;,>));   _isetType = _metadataLoadContext.Resolve(typeof(ISet&lt;>));   _stackOfTType = _metadataLoadContext.Resolve(typeof(Stack&lt;>));   _queueOfTType = _metadataLoadContext.Resolve(typeof(Queue&lt;>));   _concurrentStackType = _metadataLoadContext.Resolve(     typeof(ConcurrentStack&lt;>));   _concurrentQueueType = _metadataLoadContext.Resolve(     typeof(ConcurrentQueue&lt;>));   _idictionaryType = _metadataLoadContext.Resolve(typeof(IDictionary));   _ilistType = _metadataLoadContext.Resolve(typeof(IList));   _stackType = _metadataLoadContext.Resolve(typeof(Stack));   _queueType = _metadataLoadContext.Resolve(typeof(Queue));   _keyValuePair = _metadataLoadContext.Resolve(typeof(KeyValuePair&lt;,>));    _booleanType = _metadataLoadContext.Resolve(SpecialType.System_Boolean);   _charType = _metadataLoadContext.Resolve(SpecialType.System_Char);   _dateTimeType = _metadataLoadContext.Resolve(SpecialType.System_DateTime);   _nullableOfTType = _metadataLoadContext.Resolve(     SpecialType.System_Nullable_T);   _objectType = _metadataLoadContext.Resolve(SpecialType.System_Object);   _stringType = _metadataLoadContext.Resolve(SpecialType.System_String);    _dateTimeOffsetType = _metadataLoadContext.Resolve(typeof(DateTimeOffset));   _byteArrayType = _metadataLoadContext.Resolve(     typeof(byte)).MakeArrayType();   _guidType = _metadataLoadContext.Resolve(typeof(Guid));   _uriType = _metadataLoadContext.Resolve(typeof(Uri));   _versionType = _metadataLoadContext.Resolve(typeof(Version));   _jsonArrayType = _metadataLoadContext.Resolve(JsonArrayFullName);   _jsonElementType = _metadataLoadContext.Resolve(JsonElementFullName);   _jsonNodeType = _metadataLoadContext.Resolve(JsonNodeFullName);   _jsonObjectType = _metadataLoadContext.Resolve(JsonObjectFullName);   _jsonValueType = _metadataLoadContext.Resolve(JsonValueFullName);    \/\/ Unsupported types.   _typeType = _metadataLoadContext.Resolve(typeof(Type));   _serializationInfoType = _metadataLoadContext.Resolve(     typeof(Runtime.Serialization.SerializationInfo));   _intPtrType = _metadataLoadContext.Resolve(typeof(IntPtr));   _uIntPtrType = _metadataLoadContext.Resolve(typeof(UIntPtr));   _iAsyncEnumerableGenericType = _metadataLoadContext.Resolve(     IAsyncEnumerableFullName);   _dateOnlyType = _metadataLoadContext.Resolve(DateOnlyFullName);   _timeOnlyType = _metadataLoadContext.Resolve(TimeOnlyFullName);    _jsonConverterOfTType = _metadataLoadContext.Resolve(     JsonConverterOfTFullName);    PopulateKnownTypes(); }<\/code><\/pre>\n<p>  <\/p>\n<p>Well, how&#8217;s it going? Or maybe there is no typo at all? <\/p>\n<p>  <\/p>\n<p>Let&#8217;s first look at the analyzer warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3080\/\">V3080<\/a> Possible null dereference of method return value. Consider inspecting: Resolve(&#8230;). JsonSourceGenerator.Parser.cs 203<\/p>\n<p>  <\/p>\n<p>The <em>Resolve<\/em> method can return <em>null<\/em>. That&#8217;s what method&#8217;s signature indicates. And that&#8217;s what PVS-Studio warns us about when it detects the possibility of returning <em>null<\/em> value with the help of the interprocedural analysis.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public Type? Resolve(Type type) {   Debug.Assert(!type.IsArray,                 \"Resolution logic only capable of handling named types.\");   return Resolve(type.FullName!); }<\/code><\/pre>\n<p>  <\/p>\n<p>Let&#8217;s go further, to another overload of <em>Resolve<\/em>.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public Type? Resolve(string fullyQualifiedMetadataName) {   INamedTypeSymbol? typeSymbol =      _compilation.GetBestTypeByMetadataName(fullyQualifiedMetadataName);   return typeSymbol.AsType(this); }<\/code><\/pre>\n<p>  <\/p>\n<p>Note that <em>typeSymbol<\/em> is written as nullable reference type: <em>INamedTypeSymbol?<\/em>. Let&#8217;s go even further \u2014 to the <em>AsType<\/em> method.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public static Type AsType(this ITypeSymbol typeSymbol,                            MetadataLoadContextInternal metadataLoadContext) {   if (typeSymbol == null)   {     return null;   }    return new TypeWrapper(typeSymbol, metadataLoadContext); }<\/code><\/pre>\n<p>  <\/p>\n<p>As you can see, if the first argument is a null reference, then the <em>null<\/em> value is returned from the method.<\/p>\n<p>  <\/p>\n<p>And now let&#8217;s go back to the <em>Parser<\/em> type constructor. In this type constructor, usually the result of the <em>Resolve<\/em> method call is simply written to some field. But PVS-Studio warns that there is an exception:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">_byteArrayType = _metadataLoadContext.Resolve(typeof(byte)).MakeArrayType();<\/code><\/pre>\n<p>  <\/p>\n<p>Here, the <em>MakeArrayType<\/em> instance method is called for the result of the <em>Resolve<\/em> method call. Consequently, if <em>Resolve<\/em> returns <em>null<\/em>, a <em>NullReferenceException<\/em> will occur.<\/p>\n<p>  <\/p>\n<p><strong>Issue 6<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public abstract partial class Instrument&lt;T> : Instrument where T : struct {   [ThreadStatic] private KeyValuePair&lt;string, object?>[] ts_tags;   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3079\/\">V3079<\/a> &#8216;ThreadStatic&#8217; attribute is applied to a non-static &#8216;ts_tags&#8217; field and will be ignored Instrument.netfx.cs 20<\/p>\n<p>  <\/p>\n<p>Let&#8217;s quote <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.threadstaticattribute?view=net-6.0\">the documentation<\/a>: <em>Note that in addition to applying the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.threadstaticattribute?view=net-6.0\">ThreadStaticAttribute<\/a> attribute to a field, you must also define it as a static field (in C#) or a Shared field (in Visual Basic).<\/em><\/p>\n<p>  <\/p>\n<p>As you can see from code, the <em>ts_tags<\/em> is instance field. So, it makes no sense to mark the field with the <em>ThreadStatic<\/em> attribute. Or there&#8217;s some kind of black magic going on here\u2026 <\/p>\n<p>  <\/p>\n<p><strong>Issue 7<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">private static JsonSourceGenerationOptionsAttribute?  GetSerializerOptions(AttributeSyntax? attributeSyntax) {   ....   foreach (AttributeArgumentSyntax node in attributeArguments)   {     IEnumerable&lt;SyntaxNode> childNodes = node.ChildNodes();     NameEqualsSyntax? propertyNameNode        = childNodes.First() as NameEqualsSyntax;     Debug.Assert(propertyNameNode != null);       SyntaxNode? propertyValueNode = childNodes.ElementAtOrDefault(1);     string propertyValueStr = propertyValueNode.GetLastToken().ValueText;     ....   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3146\/\">V3146<\/a> Possible null dereference of &#8216;propertyValueNode&#8217;. The &#8216;childNodes.ElementAtOrDefault&#8217; can return default null value. JsonSourceGenerator.Parser.cs 560<\/p>\n<p>  <\/p>\n<p>If the <em>childNodes<\/em> collection contains fewer than two elements, the call of <em>ElementAtOrDefault<\/em> returns the <em>default(SyntaxNode)<\/em> value (i.e. <em>null<\/em>, since <em>SyntaxNode<\/em> is a class). In this case, a <em>NullReferenceException<\/em> is thrown on the next line. It is especially strange that <em>propertyValueNode<\/em> is a nullable reference type, but it (<em>propertyValueNode<\/em>) is dereferenced without checking.<\/p>\n<p>  <\/p>\n<p>Perhaps there is some implicit contract here that there is always more than one element in <em>childNodes<\/em>. For example, if there is <em>propertyNameNode<\/em>, then there is also <em>propertyValueNode<\/em>. In this case, to avoid unnecessary questions, one can use the <em>ElementAt<\/em> method call.<\/p>\n<p>  <\/p>\n<p><strong>Issue 8<\/strong><\/p>\n<p>  <\/p>\n<p>There is such a structure \u2013<em> Microsoft.Extensions.FileSystemGlobbing.FilePatternMatch<\/em>. This structure overrides the <em>Equals(Object)<\/em> method, which seems logical. <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/microsoft.extensions.filesystemglobbing.filepatternmatch.equals?view=dotnet-plat-ext-6.0\">Documentation describing the method.<\/a><\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/391\/4de\/0b4\/3914de0b41dc6c7603d9c4eef8f91fa3.png\" alt=\"0903_NET6\/image3.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/391\/4de\/0b4\/3914de0b41dc6c7603d9c4eef8f91fa3.png\"\/><\/p>\n<p>  <\/p>\n<p>Let&#8217;s say we have code that calls this method:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">static void FPM_Test(Object? obj) {   FilePatternMatch fpm = new FilePatternMatch();   var eq = fpm.Equals(obj); }<\/code><\/pre>\n<p>  <\/p>\n<p>What do you think will happen if <em>FPM_Test<\/em> is called with a <em>null<\/em> value? Will the <em>false<\/em> value be written to the <em>eq<\/em> variable? Well, almost.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/532\/0a0\/13d\/5320a013db44aec5319f581e18b55775.png\" alt=\"0903_NET6\/image4.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/532\/0a0\/13d\/5320a013db44aec5319f581e18b55775.png\"\/><\/p>\n<p>  <\/p>\n<p>The exception is also thrown if we pass as an argument an instance of a type other than <em>FilePatternMatch<\/em>. For example\u2026 If we pass an array of some kind.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/80d\/a0b\/8a6\/80da0b8a6f9eacbe2ec8190c98722864.png\" alt=\"0903_NET6\/image5.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/80d\/a0b\/8a6\/80da0b8a6f9eacbe2ec8190c98722864.png\"\/><\/p>\n<p>  <\/p>\n<p>Have you guessed yet why this happens? The point is, in the <em>Equals<\/em> method, the argument is not checked in any way for a <em>null<\/em> value or for type compatibility, but is simply unboxed without any conditions:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public override bool Equals(object obj) {   return Equals((FilePatternMatch) obj); }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3115\/\">V3115<\/a> Passing &#8216;null&#8217; to &#8216;Equals&#8217; method should not result in &#8216;NullReferenceException&#8217;. FilePatternMatch.cs 61<\/p>\n<p>  <\/p>\n<p>Of course, judging from the documentation, no one promised us that <em>Equals(Object)<\/em> would return <em>false<\/em> if it does not accept <em>FilePatternMatch<\/em>. But that would probably be the most expected behavior.<\/p>\n<p>  <\/p>\n<h3 id=\"duplicate-checks\">Duplicate checks<\/h3>\n<p>  <\/p>\n<p>The interesting thing about duplicate checks. You may not always explicitly know \u2014 is it just redundant code or should there be something else instead of one of duplicate checks. Anyway, let&#8217;s look at a few examples.<\/p>\n<p>  <\/p>\n<p><strong>Issue 9<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">internal DeflateManagedStream(Stream stream,                                ZipArchiveEntry.CompressionMethodValues method,                                long uncompressedSize = -1) {   if (stream == null)     throw new ArgumentNullException(nameof(stream));   if (!stream.CanRead)     throw new ArgumentException(SR.NotSupported_UnreadableStream,                                  nameof(stream));   if (!stream.CanRead)     throw new ArgumentException(SR.NotSupported_UnreadableStream,                                  nameof(stream));    Debug.Assert(method == ZipArchiveEntry.CompressionMethodValues.Deflate64);    _inflater      = new InflaterManaged(         method == ZipArchiveEntry.CompressionMethodValues.Deflate64,          uncompressedSize);    _stream = stream;   _buffer = new byte[DefaultBufferSize]; }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3021\/\">V3021<\/a> There are two &#8216;if&#8217; statements with identical conditional expressions. The first &#8216;if&#8217; statement contains method return. This means that the second &#8216;if&#8217; statement is senseless DeflateManagedStream.cs 27<\/p>\n<p>  <\/p>\n<p>At the beginning of the method, there are several checks. But, here&#8217;s the bad luck, one of the checks (<em>!stream.CanRead<\/em>) is completely duplicated (both the condition and <em>then<\/em> branch of the <em>if<\/em> statement).<\/p>\n<p>  <\/p>\n<p><strong>Issue 10<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public static object? Deserialize(ReadOnlySpan&lt;char> json,                                    Type returnType,                                    JsonSerializerOptions? options = null) {   \/\/ default\/null span is treated as empty   if (returnType == null)   {     throw new ArgumentNullException(nameof(returnType));   }    if (returnType == null)   {     throw new ArgumentNullException(nameof(returnType));   }    JsonTypeInfo jsonTypeInfo = GetTypeInfo(options, returnType);   return ReadFromSpan&lt;object?>(json, jsonTypeInfo)!; }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3021\/\">V3021<\/a> There are two &#8216;if&#8217; statements with identical conditional expressions. The first &#8216;if&#8217; statement contains method return. This means that the second &#8216;if&#8217; statement is senseless JsonSerializer.Read.String.cs 163<\/p>\n<p>  <\/p>\n<p>Yeah, a similar situation, but in a completely different place. Before using, there is the <em>returnType<\/em> parameter check for <em>null<\/em>. It&#8217;s good, but they check the parameter twice. <\/p>\n<p>  <\/p>\n<p><strong>Issue 11<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">private void WriteQualifiedNameElement(....) {   bool hasDefault = defaultValue != null &amp;&amp; defaultValue != DBNull.Value;   if (hasDefault)   {     throw Globals.NotSupported(       \"XmlQualifiedName DefaultValue not supported.  Fail in WriteValue()\");   }   ....   if (hasDefault)   {     throw Globals.NotSupported(       \"XmlQualifiedName DefaultValue not supported.  Fail in WriteValue()\");   } }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3021\/\">V3021<\/a> There are two &#8216;if&#8217; statements with identical conditional expressions. The first &#8216;if&#8217; statement contains method return. This means that the second &#8216;if&#8217; statement is senseless XmlSerializationWriterILGen.cs 102<\/p>\n<p>  <\/p>\n<p>Here the situation is a little more exciting. If the previous duplicate checks followed one after another, here they are at different ends of the method \u2014 almost 20 lines apart. However, the <em>hasDefault<\/em> local variable being checked does not change during this time. Accordingly, either the exception will be thrown during the first check, or it will not be thrown at all.<\/p>\n<p>  <\/p>\n<p><strong>Issue 12<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">internal static bool AutoGenerated(ForeignKeyConstraint fk, bool checkRelation) {   ....    if (fk.ExtendedProperties.Count > 0)     return false;    if (fk.AcceptRejectRule != AcceptRejectRule.None)     return false;   if (fk.DeleteRule != Rule.Cascade)  \/\/ &lt;=     return false;   if (fk.DeleteRule != Rule.Cascade)  \/\/ &lt;=     return false;    if (fk.RelatedColumnsReference.Length != 1)     return false;   return AutoGenerated(fk.RelatedColumnsReference[0]); }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3022\/\">V3022<\/a> Expression &#8216;fk.DeleteRule != Rule.Cascade&#8217; is always false. xmlsaver.cs 1708<\/p>\n<p>  <\/p>\n<p>Traditionally, the question is \u2014 was it needed checking another value or is it just redundant code?<\/p>\n<p>  <\/p>\n<h3 id=\"missing-interpolation\">Missing interpolation<\/h3>\n<p>  <\/p>\n<p>First, let&#8217;s have a look at a couple of warnings found. Then, I&#8217;ll tell you a little story.<\/p>\n<p>  <\/p>\n<p><strong>Issue 13<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">internal void SetLimit(int physicalMemoryLimitPercentage) {   if (physicalMemoryLimitPercentage == 0)   {     \/\/ use defaults     return;   }   _pressureHigh = Math.Max(3, physicalMemoryLimitPercentage);   _pressureLow = Math.Max(1, _pressureHigh - 9);   Dbg.Trace($\"MemoryCacheStats\",              \"PhysicalMemoryMonitor.SetLimit:                _pressureHigh={_pressureHigh}, _pressureLow={_pressureLow}\"); }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3138\/\">V3138<\/a> String literal contains potential interpolated expression. Consider inspecting: _pressureHigh. PhysicalMemoryMonitor.cs 110<\/p>\n<p>  <\/p>\n<p>It almost seems like someone wanted to log the <em>_pressureHigh<\/em> and <em>_pressureLow<\/em> fields here. However, the substitution of values won&#8217;t work, since the string is not interpolated. But the interpolation symbol is on the first argument of the <em>Dbg.Trace<\/em> method, and there is nothing to substitute in the argument. \ud83d\ude42<\/p>\n<p>  <\/p>\n<p><strong>Issue 14<\/strong><\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">private void ParseSpecs(string? metricsSpecs) {   ....   string[] specStrings = ....   foreach (string specString in specStrings)   {     if (!MetricSpec.TryParse(specString, out MetricSpec spec))     {       Log.Message(\"Failed to parse metric spec: {specString}\");     }     else     {       Log.Message(\"Parsed metric: {spec}\");       ....     }   } }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3138\/\">V3138<\/a> String literal contains potential interpolated expression. Consider inspecting: spec. MetricsEventSource.cs 381<\/p>\n<p>  <\/p>\n<p>One is trying to parse the <em>specString<\/em> string. If it doesn&#8217;t work out, one need to log the source string, if it works out \u2014 to log the result (the <em>spec<\/em> variable) and perform some other operations.<\/p>\n<p>  <\/p>\n<p>The problem again is that both in the first and in the second case the interpolation symbol is missing. As a consequence, the values of the <em>specString<\/em> and <em>spec<\/em> variables won&#8217;t be substituted.<\/p>\n<p>  <\/p>\n<p>And now get ready for the promised story.<\/p>\n<p>  <\/p>\n<p>As I mentioned above, I <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0656\/\">checked the .NET Core libraries<\/a> in 2019. I found several strings that most likely had to be interpolated, but because of the missed &#8216;$&#8217; symbol they were not. In that article, the corresponding warnings are described as issue 10 and issue 11.<\/p>\n<p>  <\/p>\n<p>I created the <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/30599\">bug report on GitHub<\/a>. After that, the .NET development team fixed some code fragments described in the article. Among them \u2014 the errors with interpolated strings. <a href=\"https:\/\/github.com\/dotnet\/corefx\/pull\/40322\/commits\/a328cc8bf763d193474fac1870c9e26a9314748a\">The corresponding pull request<\/a>.<\/p>\n<p>  <\/p>\n<p>Moreover, in the Roslyn Analyzers issue tracker, was created the <a href=\"https:\/\/github.com\/dotnet\/roslyn-analyzers\/issues\/2767\">task<\/a> of developing a new diagnostic that would detect such cases.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/1f1\/93a\/a3f\/1f193aa3fbdcafd91cc0e35122913bac.png\" alt=\"0903_NET6\/image6.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/1f1\/93a\/a3f\/1f193aa3fbdcafd91cc0e35122913bac.png\"\/><\/p>\n<p>  <\/p>\n<p>My colleague described the whole story in a little more detail <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/0659\/\">here<\/a>.<\/p>\n<p>  <\/p>\n<p>Let&#8217;s get back to the present. I knew all this and remembered it, so I was very surprised when I came across errors with missed interpolation again. How can that be? After all, there already should be the out-of-the-box diagnostic to help avoid these errors.<\/p>\n<p>  <\/p>\n<p>I decided to check out that diagnostic development issue from August 15, 2019, and it turned out\u2026 that the diagnostic is not ready yet. That&#8217;s the answer to the question \u2014 where the interpolation errors come from.<\/p>\n<p>  <\/p>\n<p>PVS-Studio has been detecting such problems since 7.03 release (June 25, 2019) \u2014 make use of it. \ud83d\ude09<\/p>\n<p>  <\/p>\n<h3 id=\"some-things-change-some-dont\">Some things change, some don&#8217;t<\/h3>\n<p>  <\/p>\n<p>During the check, I came across the warnings several times that seemed vaguely familiar to me. It turned out that I had already described them last time. Since they are still in the code, I assume that these are not errors. <\/p>\n<p>  <\/p>\n<p>For example, the code below seems to be a really unusual way to throw an <em>ArgumentOutOfRangeException<\/em>. This is issue 30 from the <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0656\/\">last check<\/a>.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">private ArrayList? _tables; private DataTable? GetTable(string tableName, string ns) {   if (_tables == null)     return _dataSet!.Tables.GetTable(tableName, ns);    if (_tables.Count == 0)     return (DataTable?)_tables[0];   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>However, I have a few questions about other fragments already discovered earlier. For example, issue 25. In the loop, the <em>seq<\/em> collection is bypassed. But only the first element of the collection, <em>seq[0]<\/em>, is constantly accessed. It looks\u2026 unusual.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public bool MatchesXmlType(IList&lt;XPathItem> seq, int indexType) {   XmlQueryType typBase = GetXmlType(indexType);    XmlQueryCardinality card = seq.Count switch   {     0 => XmlQueryCardinality.Zero,     1 => XmlQueryCardinality.One,     _ => XmlQueryCardinality.More,   };    if (!(card &lt;= typBase.Cardinality))     return false;    typBase = typBase.Prime;   for (int i = 0; i &lt; seq.Count; i++)   {     if (!CreateXmlType(seq[0]).IsSubtypeOf(typBase)) \/\/ &lt;=       return false;   }    return true; }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3102\/\">V3102<\/a> Suspicious access to element of &#8216;seq&#8217; object by a constant index inside a loop. XmlQueryRuntime.cs 729<\/p>\n<p>  <\/p>\n<p>This code confuses me a little. Does it confuse you?<\/p>\n<p>  <\/p>\n<p>Or let&#8217;s take issue 34.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public bool Remove(out int testPosition, out MaskedTextResultHint resultHint) {   ....   if (lastAssignedPos == INVALID_INDEX)   {     ....     return true; \/\/ nothing to remove.   }   ....    return true; }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3009\/\">V3009<\/a> It&#8217;s odd that this method always returns one and the same value of &#8216;true&#8217;. MaskedTextProvider.cs 1531<\/p>\n<p>  <\/p>\n<p>The method always returned <em>true<\/em> before, and it does the same thing now. At the same time, the comment says that the method can also return <em>false<\/em>: <em>Returns true on success, false otherwise<\/em>. The same story we can find in the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.componentmodel.maskedtextprovider.remove?view=net-6.0\">documentation<\/a>.<\/p>\n<p>  <\/p>\n<p>The following example I will even put in a separate section. Even though it was also described in the previous article. Let&#8217;s speculate a little not only on the code fragment itself, but also on one feature used in the fragment \u2013 nullable reference types.<\/p>\n<p>  <\/p>\n<h3 id=\"about-nullable-reference-types-again\">About nullable reference types again<\/h3>\n<p>  <\/p>\n<p>In general, I have not yet figured out whether I like nullable reference types or not. <\/p>\n<p>  <\/p>\n<p>On the one hand, nullable reference types have a huge advantage. They make signature of methods more informative. One glance at a method is enough to understand whether it can return <em>null<\/em>, whether a certain parameter can have a <em>null<\/em> value, etc. <\/p>\n<p>  <\/p>\n<p>On the other hand, all this is built on trust. No one forbids you to write code like this:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">static String GetStr() {   return null!; }  static void Main(string[] args) {   String str = GetStr();   Console.WriteLine(str.Length); \/\/ NRE, str - null }<\/code><\/pre>\n<p>  <\/p>\n<p>Yes, yes, yes, it&#8217;s synthetic code, but you can write it this way! If such a code is written inside your company, we go (relatively speaking) to the author of <em>GetStr<\/em> and have a conversation. However, if <em>GetStr<\/em> is taken from some library and you don&#8217;t have the sources of this library \u2014 such a surprise won&#8217;t be very pleasant.<\/p>\n<p>  <\/p>\n<p>Let&#8217;s return from synthetic examples to our main topic \u2013 .NET 6. And there are subtleties. For example, different libraries are divided into different solutions. And looking through them, I repeatedly wondered \u2013 is nullable context enabled in this project? The fact that there is no check for <em>null<\/em> \u2014 is this expected or not? Probably, this is not a problem when working within the context of one project. However, with cursory analysis of all projects, it creates certain difficulties.<\/p>\n<p>  <\/p>\n<p>And it really gets interesting. All sorts of strange things start showing up when there is migration to a nullable context. It seems like a variable cannot have <em>null<\/em> value, and at the same time there is a check. And let&#8217;s face it, .NET has a few such places. Let me show you a couple of them.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">private void ValidateAttributes(XmlElement elementNode) {   ....   XmlSchemaAttribute schemaAttribute      = (_defaultAttributes[i] as XmlSchemaAttribute)!;   attrQName = schemaAttribute.QualifiedName;   Debug.Assert(schemaAttribute != null);   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>PVS-Studio warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3095\/\">V3095<\/a> The &#8216;schemaAttribute&#8217; object was used before it was verified against null. Check lines: 438, 439. DocumentSchemaValidator.cs 438<\/p>\n<p>  <\/p>\n<p>The &#8216;!&#8217; symbol hints that we are working with a nullable context here. Okay.<\/p>\n<p>  <\/p>\n<p>1. Why is the &#8216;as&#8217; operator used for casting, and not a direct cast? If there is confidence that <em>schemaAttribute<\/em> is not <em>null<\/em> (that&#8217;s how I read the implicit contract with &#8216;!&#8217;), so <em>_defaultAttributes[i]<\/em> does have the <em>XmlSchemaAttribute<\/em> type. Well, let&#8217;s say a developer likes this syntax more \u2014 okay.<\/p>\n<p>  <\/p>\n<p>2. If <em>schemaAttribute<\/em> is not <em>null<\/em>, why is there the check for <em>null<\/em> in <em>Debug.Assert<\/em> below?<\/p>\n<p>  <\/p>\n<p>3. If the check is relevant and <em>schemaAttribute<\/em> can still have a <em>null<\/em> value (contrary to the semantics of nullable reference types), then execution will not reach <em>Debug.Assert<\/em> due to the thrown exception. The exception will be thrown when accessing <em>schemaAttribute.QualifiedName<\/em>.<\/p>\n<p>  <\/p>\n<p>Personally, I have a lot of questions at once when looking at such a small piece of code. <\/p>\n<p>  <\/p>\n<p>Here is a similar story:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">public Node DeepClone(int count) {   ....   while (originalCurrent != null)   {     originalNodes.Push(originalCurrent);     newNodes.Push(newCurrent);     newCurrent.Left = originalCurrent.Left?.ShallowClone();     originalCurrent = originalCurrent.Left;     newCurrent = newCurrent.Left!;   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>On the one hand, <em>newCurrent.Left<\/em> can have a <em>null<\/em> value, since the result of executing the <em>?.<\/em>operator is written to it (<em>originalCurrent.Left?.ShallowClone()<\/em>). On the other hand, in the last line we see the annotation that <em>newCurrent.Left<\/em> not <em>null<\/em>.<\/p>\n<p>  <\/p>\n<p>And now let&#8217;s look at the code fragment from .NET 6, that in fact, was the reason why I started to write this section. The <em>IStructuralEquatable.Equals(object? other, IEqualityComparer comparer)<\/em> implementation in the <em>ImmutableArray&lt;T><\/em> type.<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">internal readonly T[]? array; bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) {   var self = this;   Array? otherArray = other as Array;   if (otherArray == null)   {     if (other is IImmutableArray theirs)     {       otherArray = theirs.Array;        if (self.array == null &amp;&amp; otherArray == null)       {         return true;       }       else if (self.array == null)       {         return false;       }     }   }    IStructuralEquatable ours = self.array!;   return ours.Equals(otherArray, comparer); }<\/code><\/pre>\n<p>  <\/p>\n<p>If you look at the last code lines in Visual Studio, the editor will helpfully tell you that <em>ours<\/em> is not <em>null<\/em>. It can be seen from the code \u2013 <em>self.array<\/em> is nonnullable reference variable. <\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/558\/8c6\/b01\/5588c6b01039cb6ff3abee72212101c6.png\" alt=\"0903_NET6\/image7.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/558\/8c6\/b01\/5588c6b01039cb6ff3abee72212101c6.png\"\/><\/p>\n<p>  <\/p>\n<p>OK, let&#8217;s write the following code:<\/p>\n<p>  <\/p>\n<pre><code class=\"cs\">IStructuralEquatable immutableArr = default(ImmutableArray&lt;String>); var eq = immutableArr.Equals(null, EqualityComparer&lt;String>.Default);<\/code><\/pre>\n<p>  <\/p>\n<p>Then we run it for execution and see a <em>NullReferenceException<\/em>.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/14d\/072\/4aa\/14d0724aae3f1e087001cbeea4f97dab.png\" alt=\"0903_NET6\/image8.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/14d\/072\/4aa\/14d0724aae3f1e087001cbeea4f97dab.png\"\/><\/p>\n<p>  <\/p>\n<p>Whoops. It seems that the <em>ours<\/em> variable, which is not <em>null<\/em>, in fact still turned out to be a null reference.<\/p>\n<p>  <\/p>\n<p>Let&#8217;s find out how that happened.<\/p>\n<p>  <\/p>\n<ol>\n<li>The <em>array<\/em> field of the <em>immutableArr<\/em> object takes the default <em>null<\/em> value.<\/li>\n<li><em>other<\/em> has a <em>null<\/em> value, so <em>otherArray<\/em> also has a <em>null<\/em> value.<\/li>\n<li>The check of<em> other is ImmutableArray <\/em>gives<em> false<\/em>.<\/li>\n<li>At the time of writing the value to <em>ours<\/em>, the <em>self.array<\/em> field is <em>null<\/em>.<\/li>\n<li>You know the rest.<\/li>\n<\/ol>\n<p>  <\/p>\n<p>Here you can have the counter-argument that the immutable array has incorrect state, since it was created not through special methods\/properties, but through calling the <em>default<\/em> operator. But getting an NRE on an <em>Equals<\/em> call for such an object is still a bit strange. <\/p>\n<p>  <\/p>\n<p>However, that&#8217;s not even the point. Code, annotations and hints indicates that <em>ours<\/em> is not <em>null<\/em>. In fact, the variable does have the <em>null<\/em> value. For me personally, this undermines trust in nullable reference types a bit.<\/p>\n<p>  <\/p>\n<p>PVS-Studio issues a warning: <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v3125\/\">V3125<\/a> The &#8216;ours&#8217; object was used after it was verified against null. Check lines: 1144, 1136. ImmutableArray_1.cs 1144<\/p>\n<p>  <\/p>\n<p>By the way, I wrote about this problem in the <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0656\/\">last article<\/a> (issue 53). Then, however, there was no nullable annotations yet.<\/p>\n<p>  <\/p>\n<p><strong>Note<\/strong>. Returning to the conversation about operations on the <em>ImmutableArray&lt;T><\/em> instances in the default state, some methods\/properties use special methods: <em>ThrowNullRefIfNotInitialized<\/em> and<em>ThrowInvalidOperationIfNotInitialized<\/em>. These methods report the uninitialized state of the object. Moreover, explicit implementations of interface methods use <em>ThrowInvalidOperationIfNotInitialized<\/em>. Perhaps it should have been used in the case described above.<\/p>\n<p>  <\/p>\n<p>Here I want to ask our audience \u2013 what kind of experience do you have working with nullable reference types? Do you like them? Or maybe you don&#8217;t like them? Have you used nullable reference types on your projects? What went well? What difficulties did you have? I&#8217;m curious as to your view on nullable reference types.<\/p>\n<p>  <\/p>\n<p>By the way, my colleagues already wrote about nullable reference types in a couple of articles: <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0631\/\">one<\/a>, <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0764\/\">two<\/a>. Time goes on, but the issue is still debatable.<\/p>\n<p>  <\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>  <\/p>\n<p>In conclusion, once again, I would like to congratulate the .NET 6 development team with the release. I also want to say thank you to all those who contribute to this project. I am sure that they will fix the shortcomings. There are still many achievements ahead.<\/p>\n<p>  <\/p>\n<p>I also hope that I was able to remind you once again how the static analysis benefits the development process. If you are interested, you can try PVS-Studio on your project as well. By the way, click on <a href=\"https:\/\/pvs-studio.com\/net6_checking\">this link<\/a>, and get an extended license that is valid for 30 days, not 7. Isn&#8217;t that a good reason to try the analyzer? \ud83d\ude09<\/p>\n<p>  <\/p>\n<p>And by good tradition, I invite you to subscribe to <a href=\"https:\/\/twitter.com\/_SergVasiliev_\">my Twitter<\/a> so as not to miss anything interesting.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/598165\/\"> https:\/\/habr.com\/ru\/articles\/598165\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/7a6\/522\/913\/7a6522913a700269aad35cfc06f0caa5.png\" alt=\"0903_NET6\/image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/7a6\/522\/913\/7a6522913a700269aad35cfc06f0caa5.png\"\/><\/p>\n<p>  <\/p>\n<p>The .NET 6 turned out to be much-awaited and major release. If you write for .NET, you could hardly miss such an event. We also couldn&#8217;t pass by the new version of this platform. We decided to check what interesting things we can find in the sources of .NET libraries.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-390689","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/390689","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=390689"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/390689\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=390689"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=390689"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=390689"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}