void PaintWorker(PaintEventArgs e, bool up, CheckState state) { up = up && state == CheckState.Unchecked; .... if (up & IsHighContrastHighlighted()) { .... } else if (up & IsHighContrastHighlighted()) { .... } else { .... } .... }

protected bool IsHighContrastHighlighted() { return SystemInformation.HighContrast && Application.RenderWithVisualStyles && (Control.Focused || Control.MouseIsOver || (Control.IsDefault && Control.Enabled)); }

public int SelectionCharOffset { get { int selCharOffset = 0; .... NativeMethods.CHARFORMATA cf = GetCharFormat(true); // if the effects member contains valid info if ((cf.dwMask & RichTextBoxConstants.CFM_OFFSET) != 0) { selCharOffset = cf.yOffset; // <= } else { // The selection contains characters of different offsets, // so we just return the offset of the first character. selCharOffset = cf.yOffset; // <= } .... } .... }

V3004 The 'then' statement is equivalent to the 'else' statement. SplitContainer.cs 1700

V3004 The 'then' statement is equivalent to the 'else' statement. ToolstripProfessionalRenderer.cs 371

internal void InitSystemColors(ref Dictionary<KnownColors, Color> rgbTable) { .... rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = buttonFace; rgbTable[ProfessionalColorTable.KnownColors.msocbvcrCBBdrOuterDocked] = buttonShadow; .... }

We like to search for errors in Microsoft projects. Why? It's simple: their projects are usually easy to check (you can work in Visual Studio environment for which PVS-Studio has a convenient plugin) and they contain few errors. That's why the usual work algorithm is as follows: find and download an open source project from MS; check it; choose interesting errors; make sure there are few of them; write an article without forgetting to praise the developers. Great! Win-win-win: it took a little time, the bosses are glad to see new materials in the blog, and karma is fine. But this time «something went wrong». Let's see what we have found in the source code of Windows Forms and whether we should speak highly of Microsoft this time.In early December 2018, Microsoft announced the release of the .NET Core 3 Preview 1. A little earlier (about mid-October), GitHub started to actively disclose the sources of Windows Forms — the .NET Core UI platform for creating Windows desktop applications. You can see the commit statistics here . Now anyone can download the WinForms source code for review.I also downloaded the sources to search for errors there with PVS-Studio. The check did not cause any difficulties. We needed: Visual Studio 2019, .NET Core 3.0 SDK Preview, PVS-Studio. And here we have the log of the analyzer's warnings.Having received the PVS-Studio report, I usually sort it by diagnostic numbers in the ascending order (the window with the PVS-Studio message log in Visual Studio environment has various options of sorting and filtering the list). It allows you to work with groups of similar errors, which greatly simplifies source code analysis. I mark interesting errors in the list with a «star» and only then, after analyzing the whole log, I write out code fragments and describe them. Since there are usually few errors, I «stir» them trying to place the most interesting ones at the beginning and end of the article. But this time it turned out to be a lot of errors (eh, the intrigue has not been saved for a long time) and I will cite them in the order of numbers of diagnostics.What did we find? 833 High and Medium warnings (249 and 584, respectively) were issued for 540,000 lines of code (not including empty ones) in 1670 cs files. And yes, traditionally I didn't check the tests and didn't consider the Low warnings (there were 215 of them). According to my previous observations, the warnings are too many for the MS project. But not all the warnings are errors.For this project the number of false alarms was about 30%. In about 20% of cases, I just could not make an exact conclusion whether it was an error or not because I was not familiar with the code well enough. And at least 20% of the errors I missed can be written off as «human factor»: haste, tiredness, etc. By the way, the opposite effect is also possible: some same-type triggers, the number of which could reach 70-80, I looked «next but one», which sometimes could increase the number of errors that I thought were real.Anyway, 30% of the warnings indicate real errors, which is quite a large percentage if you take into account that the analyzer was not pre-configured.So, the number of errors I managed to find was about 240, which is within the range of the given statistics. Again, in my opinion, this is not the most outstanding result for a MS project (although it will make only 0.44 errors per 1000 code lines) and there are probably more real errors in WinForms code as well. I suggest considering the reasons at the end of the article and now let's see the most interesting errors.PVS-Studio: V3003 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 213, 224. ButtonStandardAdapter.cs 213andblocks check the same condition. It looks like copy-paste. Is it an error? If you look at the declaration of themethod, you may doubt it:The method can probably return different values for sequential calls. And what is happening in the caller method, of course, looks strange, but has the right to exist. However, I would advise the authors to take a look at this code fragment. Just in case. It is also a good example of how difficult it is to draw conclusions when analyzing unfamiliar code.PVS-Studio: V3004 The 'then' statement is equivalent to the 'else' statement. RichTextBox.cs 1018And there is definitely a copy-paste error here. Regardless of the condition, thevariable will always get the same value.There are two more such errors in WinForms code:PVS-Studio: V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 681, 680. ProfessionalColorTable.cs 681The method fills thedictionary. The analyzer pointed to a code fragment where different values are written twice on the same key in sequence. Things would be fine but there are still 16 such fragments in this method. It does not look like a one-of-a-kind error anymore. But why they do this remains a mystery to me. I didn't find any signs of autogenerated code. It looks like this in the editor:

V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 785, 784. ProfessionalColorTable.cs 785 V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 787, 786. ProfessionalColorTable.cs 787 V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 789, 788. ProfessionalColorTable.cs 789 V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 791, 790. ProfessionalColorTable.cs 791 V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 797, 796. ProfessionalColorTable.cs 797 V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 799, 798. ProfessionalColorTable.cs 799 V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 807, 806. ProfessionalColorTable.cs 807 V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 815, 814. ProfessionalColorTable.cs 815 V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 817, 816. ProfessionalColorTable.cs 817 V3008 The variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 823, 822. ProfessionalColorTable.cs 823

private void CheckHierarchyState() { if (checkHierarchy && listManager != null && myGridTable != null) { if (myGridTable == null) // <= { // there was nothing to check return; } for (int j = 0; j < myGridTable.GridColumnStyles.Count; j++) { DataGridColumnStyle gridColumn = myGridTable.GridColumnStyles[j]; } checkHierarchy = false; } }

public int Compare(object left, object right) { OrderedCodeStatementCollection cscLeft = left as OrderedCodeStatementCollection; OrderedCodeStatementCollection cscRight = right as OrderedCodeStatementCollection; if (left == null) { return 1; } else if (right == null) { return -1; } else if (right == left) { return 0; } return cscLeft.Order - cscRight.Order; // <= }

void ISelectionService.SetSelectedComponents( ICollection components, SelectionTypes selectionType) { .... // Handle the click case object requestedPrimary = null; int primaryIndex; if (fPrimary && 1 == components.Count) { foreach (object o in components) { requestedPrimary = o; if (o == null) { throw new ArgumentNullException(nameof(components)); } break; } } .... }

public State OcxState { .... set { .... if (value == null) { return; } .... ocxState = value; if (ocxState != null) // <= { axState[manualUpdate] = ocxState._GetManualUpdate(); licenseKey = ocxState._GetLicenseKey(); } else { axState[manualUpdate] = false; licenseKey = null; } .... } }

public override object EditValue(....) { .... ImageEditor e = ....; Type myClass = GetType(); if (!myClass.Equals(e.GetType()) && e != null && myClass.IsInstanceOfType(e)) { .... } .... }

internal void EnterInSituEdit(ToolStripItem toolItem) { .... ToolStripDropDownItem dropDownItem = toolItem as ToolStripDropDownItem; if (!(dropDownItem.Owner is ToolStripDropDownMenu) && dropDownItem != null && dropDownItem.Bounds.Width < commitedEditorNode.Bounds.Width) { .... } .... }

internal ColumnHeader InsertColumn( int index, ColumnHeader ch, bool refreshSubItems) { .... // Add the column to our internal array int columnCount = (columnHeaders == null ? 0 : columnHeaders.Length); if (columnCount > 0) { ColumnHeader[] newHeaders = new ColumnHeader[columnCount + 1]; if (columnCount > 0) { System.Array.Copy(columnHeaders, 0, newHeaders, 0, columnCount); } .... } .... }

int UnsafeNativeMethods.IOleInPlaceSite.GetWindowContext( out UnsafeNativeMethods.IOleInPlaceFrame ppFrame, out UnsafeNativeMethods.IOleInPlaceUIWindow ppDoc, NativeMethods.COMRECT lprcPosRect, NativeMethods.COMRECT lprcClipRect, NativeMethods.tagOIFI lpFrameInfo) { ppDoc = null; ppFrame = Host.GetParentContainer(); lprcPosRect.left = Host.Bounds.X; lprcPosRect.top = Host.Bounds.Y; .... lprcClipRect = WebBrowserHelper.GetClipRect(); // <= if (lpFrameInfo != null) { lpFrameInfo.cb = Marshal.SizeOf<NativeMethods.tagOIFI>(); lpFrameInfo.fMDIApp = false; .... } return NativeMethods.S_OK; }

protected override void OnMouseMove(DataGridViewCellMouseEventArgs e) { .... dgvabsEffective = AdjustCellBorderStyle( DataGridView.AdvancedCellBorderStyle, dgvabsPlaceholder, singleVerticalBorderAdded, singleHorizontalBorderAdded, isFirstDisplayedRow, // <= isFirstDisplayedColumn); // <= .... }

public virtual DataGridViewAdvancedBorderStyle AdjustCellBorderStyle( DataGridViewAdvancedBorderStyledataGridViewAdvancedBorderStyleInput, DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder, bool singleVerticalBorderAdded, bool singleHorizontalBorderAdded, bool isFirstDisplayedColumn, bool isFirstDisplayedRow) { .... }

internal static class NativeMethods { .... public static readonly int LOCALE_USER_DEFAULT = MAKELCID(LANG_USER_DEFAULT); public static readonly int LANG_USER_DEFAULT = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); .... }

internal static class NativeMethods { public static readonly int LOCALE_USER_DEFAULT = MAKELCID(LANG_USER_DEFAULT); public static readonly int LANG_USER_DEFAULT = MAKELANGID(0x00, 0x01); public static int MAKELANGID(int primary, int sub) { return ((((ushort)(sub)) << 10) | (ushort)(primary)); } public static int MAKELCID(int lgid) { return MAKELCID(lgid, 0x0); } public static int MAKELCID(int lgid, int sort) { return ((0xFFFF & lgid) | (((0x000f) & sort) << 16)); } } class Program { static void Main() { System.Console.WriteLine(NativeMethods.LOCALE_USER_DEFAULT); } }

protected void DeserializeStatement( IDesignerSerializationManager manager, CodeStatement statement) { .... CodeExpressionStatement ces = statement as CodeExpressionStatement; if (ces != null) { .... } else { .... DeserializeExpression(manager, null, ces.Expression); // <= .... } .... }

public void ValidateOwnerDrawRegions(ComboBox comboBox, ....) { .... if (comboBox != null) { return; } Rectangle topOwnerDrawArea = new Rectangle(0, 0, comboBox.Width, innerBorder.Top); .... }

private void EnsureResData() { .... XmlTextReader contentReader = null; try { if (fileContents != null) { contentReader = new XmlTextReader(....); } else if (reader != null) { contentReader = new XmlTextReader(....); } else if (fileName != null || stream != null) { .... contentReader = new XmlTextReader(....); } SetupNameTable(contentReader); // <= .... } finally { .... } .... }

private void SetupNameTable(XmlReader reader) { reader.NameTable.Add(ResXResourceWriter.TypeStr); reader.NameTable.Add(ResXResourceWriter.NameStr); .... }

private static Rectangle GetAnchorDestination( IArrangedElement element, Rectangle displayRect, bool measureOnly) { .... AnchorInfo layout = GetAnchorInfo(element); int left = layout.Left + displayRect.X; .... }

private static AnchorInfo GetAnchorInfo(IArrangedElement element) { return (AnchorInfo)element.Properties.GetObject(s_layoutInfoProperty); } public object GetObject(int key) => GetObject(key, out _); public object GetObject(int key, out bool found) { short keyIndex = SplitKey(key, out short element); if (!LocateObjectEntry(keyIndex, out int index)) { found = false; return null; } // We have found the relevant entry. See if // the bitmask indicates the value is used. if (((1 << element) & s_objEntries[index].Mask) == 0) { found = false; return null; } found = true; switch (element) { case 0: return s_objEntries[index].Value1; .... default: Debug.Fail("Invalid element obtained from LocateObjectEntry"); return null; } }

internal class PropertyNames { .... public static readonly string ShowImageMargin = "ShowCheckMargin"; ... public static readonly string ShowCheckMargin = "ShowCheckMargin"; .... }

I'll give you the first ten warnings on the list:PVS-Studio: V3011 Two opposite conditions were encountered. The second condition is always false. Check lines: 5242, 5240. DataGrid.cs 5242Theoperator will never be executed. Most likely, thecondition in the externalblock was added later during refactoring. And now the check ofis meaningless. To improve the code quality, you should remove this check.PVS-Studio: V3019 Possibly an incorrect variable is compared to null after type conversion using 'as' keyword. Check variables 'left', 'cscLeft'. TypeCodeDomSerializer.cs 611PVS-Studio: V3019 Possibly an incorrect variable is compared to null after type conversion using 'as' keyword. Check variables 'right', 'cscRight'. TypeCodeDomSerializer.cs 615The analyzer generated two warnings for themethod at once. What is the problem? It is thatandvalues are not checked forat all. They may get this value after unsuccessful casting to thetype. Then an exception will be thrown in the lastexpression. This situation is possible when all the checks forandpass and do not lead to a preliminary exit from the method.To fix the code, you should useinstead ofeverywhere.PVS-Studio: V3020 An unconditional 'break' within a loop. SelectionService.cs 421This fragment refers rather to the «code smell». There is no error here. But questions arise about the way theloop is organized. It is clear why it is needed here: because of the need to extract elements of the collection, passed as. But why did the loop, initially designed for single iteration (the precondition is the presence of a single element in the collection), require additional support such as? Probably, the answer can be considered as follows: «Historically, this has come to be». The code looks ugly.PVS-Studio: V3022 Expression 'ocxState != null' is always true. AxHost.cs 2186Because of a logical error, «dead code» occurred in this fragment. Expressions in theblock will never be executed.PVS-Studio: V3027 The variable 'e' was utilized in the logical expression before it was verified against null in the same logical expression. ImageEditor.cs 99Variablein the condition is first used and then checked against. Hello,One more such error:PVS-Studio: V3027 The variable 'dropDownItem' was utilized in the logical expression before it was verified against null in the same logical expression. ToolStripMenuItemDesigner.cs 1351The situation is similar to the previous one but with thevariable. I think that such errors appear as a result of careless refactoring. Probably, a part of the conditionwas added into the code later.PVS-Studio: V3030 Recurring check. The 'columnCount > 0' condition was already verified in line 3900. ListView.cs 3903A mistake that may seem harmless. Indeed, an unnecessary check is performed which does not affect the operational logic. And sometimes it is even done when you need to check the state of some visual component again, for example, getting the number of entries in the list. But in this case thevariableis checked twice. It is very suspicious. Either they wanted to check another variable or they used a wrong condition in one of the checks.PVS-Studio: V3061 Parameter 'lprcClipRect' is always rewritten in method body before being used. WebBrowserSiteBase.cs 281An unevident mistake. Yes, theparameter is actually initialized with a new value without using it in any way. But what does it lead to in the end? I think that somewhere in the calling code the reference passed through this parameter will remain unchanged, although it was not intended to be so. Really, appreciate the handling of other variables in this method. Even its name («Get» prefix) hints that some initialization will be performed inside the method through passed parameters. And it is so. The first two parameters (and) are passed with themodifier and they get new values. Referencesandare used to access and initialize class fields. Onlystands out. Probably, theormodifier is required for this parameter.PVS-Studio: V3066 Possible incorrect order of arguments passed to 'AdjustCellBorderStyle' method: 'isFirstDisplayedRow' and 'isFirstDisplayedColumn'. DataGridViewComboBoxCell.cs 1934The analyzer suspected that the last two arguments were mixed up. Let's take a look at the declaration of themethod:Looks like a mistake. Yes, some arguments are often passed in reverse order, for example, to exchange some variables. But I don't think this is the case. Nothing in the caller or callee methods indicates this usage pattern. First, variables of thetype are mixed up. Second, the names of the methods are also regular: no «Swap» or «Reverse». Besides, it is not so difficult to make a mistake like that. People often perceive the order of the «row/column» pair differently. For me, for example, it is the «row/column» that is familiar. But for the author of the method called, obviously, the more usual order is «column/row».PVS-Studio: V3070 Uninitialized variable 'LANG_USER_DEFAULT' is used when initializing the 'LOCALE_USER_DEFAULT' variable. NativeMethods.cs 890Rare mistake. The initialization order of class fields is mixed up. To calculate the value of the fieldthefield is used, which is not yet initialized and has a value of 0. By the way, thevariable is not used anywhere else in the code. I went an extra mile and wrote a small console program that simulates the situation. I substituted some constants used in WinForms code with their actual values:As a result, the console will display: 0. Now let's swap the declarations of theandfields. The result of the program execution is as follows: 1024. I think there is nothing more to comment on here.PVS-Studio: V3080 Possible null dereference. Consider inspecting 'ces'. CodeDomSerializerBase.cs 562The code that should «crash» rather regularly, because you can get into thebranch just when thereference equalsAnother similar example:PVS-Studio: V3080 Possible null dereference. Consider inspecting 'comboBox'. ComboBox.cs 6610The paradoxical code. Apparently, thecheck was confused with. And so, we will get anotherWe have considered two rather obvious V3080 errors where you can visually trace a potential null reference usage within a method. But the V3080 diagnostic is much more efficient and can find such errors for method call chains. Not so long ago we have significantly improved the dataflow and interprocedural analysis mechanisms. You may read about this in the article " Nullable Reference types in C# 8.0 and static analysis ". But here is such kind of error detected in WinForms:PVS-Studio: V3080 Possible null dereference inside method at 'reader.NameTable'. Consider inspecting the 1st argument: contentReader. ResXResourceReader.cs 267Look what happens to thevariable in the method body. After initialization with null, it will be initialized again in one of the checks. But the series of checks does not end with theblock. It means that in some rare case (or due to refactoring in the future) the referencestill remain null. Then it will be passed to themethod where it is used without any check:This is potentially unsafe code.And one more error where the analyzer had to go through the call chain to detect the problem:PVS-Studio: V3080 Possible null dereference. Consider inspecting 'layout'. DockAndAnchorLayout.cs 156The analyzer claims that it is possible to get a null reference from themethod, which will cause an exception when calculating thevalue. Let's go through the whole call chain and check if it is true:Indeed, in some cases, themethod that ends the call chain will return, which will be passed to the caller method without any additional checks. Probably, it is necessary to cover such a situation in themethod.There are quite a lot of such errors in WinForms code,. They all look alike and I will not describe them in the article.PVS-Studio: V3091 Empirical analysis. It is possible that a typo is present inside the string literal: «ShowCheckMargin». The 'ShowCheckMargin' word is suspicious. PropertyNames.cs 136A good example of an error that is not so easy to find. When initializing the class fields the same value is used although the author of the code obviously did not intend so (copy-paste is to blame). The analyzer made this conclusion by comparing the names of variables and values of assigned strings. I have given only lines with errors but you should check it out how it looks in the code editor:

private void RunMessageLoopInner(int reason, ApplicationContext context) { .... hwndOwner = new HandleRef( null, UnsafeNativeMethods.GetWindowLong( new HandleRef(currentForm, currentForm.Handle), // <= NativeMethods.GWL_HWNDPARENT)); .... if (currentForm != null && ....) .... }

public Color BackgroundColor { .... set { .... if (!value.Equals(backgroundBrush.Color)) // <= { if (backgroundBrush != null && BackgroundBrush != DefaultBackgroundBrush) .... } } }

private void SetPropValue(object value) { .... if (....) { if .... else if (_propInfo != null) .... } else { _propInfo.SetValue(_control, value); } .... }

public int BorderSize { .... set { .... if (owner != null && owner.ParentInternal != null) { LayoutTransaction.DoLayoutIf(....); } owner.Invalidate(); // <= .... } }

sealed partial class DeviceContext : .... { WindowsFont selectedFont; .... internal void DisposeFont(bool disposing) { if (disposing) { DeviceContexts.RemoveDeviceContext(this); } if (selectedFont != null && selectedFont.Hfont != IntPtr.Zero) { IntPtr hCurrentFont = IntUnsafeNativeMethods.GetCurrentObject( new HandleRef(this, hDC), IntNativeMethods.OBJ_FONT); if (hCurrentFont == selectedFont.Hfont) { // select initial font back in IntUnsafeNativeMethods.SelectObject(new HandleRef(this, Hdc), new HandleRef(null, hInitialFont)); hCurrentFont = hInitialFont; // <= } selectedFont.Dispose(disposing); selectedFont = null; } } .... }

public IntPtr SelectFont(WindowsFont font) { .... hCurrentFont = font.Hfont; .... } public void ResetFont() { .... hCurrentFont = hInitialFont; }

sealed partial class DeviceContext : .... { .... IntPtr hInitialFont; .... IntPtr hCurrentFont; // <= .... }

Detection of such errors is what demonstrates all the power and endless attention span of static analysis tools.PVS-Studio: V3095 The 'currentForm' object was used before it was verified against null. Check lines: 3386, 3404. Application.cs 3386This is classic. Thevariable is used without any checks. But then it's checked forin the code. In this case I can advise you to be more attentive when working with reference types and also use static analyzers :).One more such error:PVS-Studio: V3095 The 'backgroundBrush' object was used before it was verified against null. Check lines: 2331, 2334. DataGrid.cs 2331In WinForms code, I came acrosssuch errors. In my opinion, all of them are rather critical and require attention of developers. But it is not so interesting to tell about them in the article anymore, so I will limit myself to the two mentioned above.PVS-Studio: V3125 The '_propInfo' object was used and was verified against null in different execution branches. Check lines: 996, 982. Binding.cs 996For the completeness sake — also a kind of classic, error V3125 . The opposite situation. At first, the developer uses a potentiallyreference safely, having checked it against null, but stops doing it further in the code.And one more such error:PVS-Studio: V3125 The 'owner' object was used after it was verified against null. Check lines: 64, 60. FlatButtonAppearance.cs 64Lovely. But this an outside researcher's standpoint. After all, the analyzer foundsuch patterns in WinForms code besides these two V3125 . Developers have a lot to work on.And finally, there is an interesting error, in my opinion.PVS-Studio: V3137 The 'hCurrentFont' variable is assigned but is not used by the end of the function. DeviceContext2.cs 241Let's see what alerted the analyzer, and why it may indicate a problem that a variable is assigned a value, but never used in the code.Thefile contains a partial class. Themethod is used to free resources after working with graphics: device context and fonts. For a better understanding I have given the wholemethod. Pay attention to the local variable. The problem is that the declaration of this variable in the method hides the class field of the same name. I found two methods of theclass where thewith the nameis used:Look at themethod. The last line there is exactly what themethod does in the subblock(this is what the analyzer points to). Thisfield of the same name is declared in another part of the partial class in thefile:Thus, an obvious mistake was made. Another question is in its importance. Now, as a result of themethod's work in the section marked with the comment «select initial font back in», thefield will not be initialized. I think only the authors of the code can give an exact verdict.So, this time, I'm gonna have to criticize MS a little bit. In WinForms, there are a lot of errors that require close attention of developers. Perhaps it is the fault of some haste with which MS work on .NET Core 3 and components, including WinForms. In my opinion, the WinForms code is still «raw», but I hope that the situation will change for the better soon.The second reason for the large number of errors may be that our analyzer has simply become better at searching for them :).By the way, an article of my colleague Sergey Vasiliev will soon be published in which he searches and finds quite a lot of problems in the code of .NET Core libraries. I hope that his work will also contribute to improving the characteristics of the .NET platform, because we always try to inform the developers about the results of their projects' analysis.And for those who want to improve their products on their own or search for errors in other people's projects, I suggest that you download and try PVS-Studio Clean code to everyone!