DEV Community

Sergey Vasiliev
Sergey Vasiliev

Posted on

.NET 7: suspicious places and errors in the source code

1015_NET7_Errors/image1.png

.NET 7 has been released! It's time for us to dig into its source code and start looking for errors and strange code fragments. In this article, you'll see comments on our findings from the .NET developers. After all, they know the platform code better than anyone else. Buckle up!

I analyzed the release code of .NET 7. You can find it on GitHub: link.

There were two release candidates (RC) prior to the main release, so most of the bugs must have been fixed. It's more interesting that way — we can investigate whether some of them have gotten into production.

I created an issue on GitHub for each suspicious code fragment. This helped me understand which ones are redundant, which ones are incorrect, and what was fixed by developers.

Issue 1

Can you spot an error here? Let's check!

internal sealed record IncrementalStubGenerationContext(
  StubEnvironment Environment,
  SignatureContext SignatureContext,
  ContainingSyntaxContext ContainingSyntaxContext,
  ContainingSyntax StubMethodSyntaxTemplate,
  MethodSignatureDiagnosticLocations DiagnosticLocation,
  ImmutableArray<AttributeSyntax> ForwardedAttributes,
  LibraryImportData LibraryImportData,
  MarshallingGeneratorFactoryKey<
    (TargetFramework, Version, LibraryImportGeneratorOptions)
  > GeneratorFactoryKey,
  ImmutableArray<Diagnostic> Diagnostics)
{
  public bool Equals(IncrementalStubGenerationContext? other)
  {
    return    other is not null
           && StubEnvironment.AreCompilationSettingsEqual(Environment, 
                                                          other.Environment)
           && SignatureContext.Equals(other.SignatureContext)
           && ContainingSyntaxContext.Equals(other.ContainingSyntaxContext)
           && StubMethodSyntaxTemplate.Equals(other.StubMethodSyntaxTemplate)
           && LibraryImportData.Equals(other.LibraryImportData)
           && DiagnosticLocation.Equals(DiagnosticLocation)
           && ForwardedAttributes.SequenceEqual(other.ForwardedAttributes, 
                (IEqualityComparer<AttributeSyntax>)
                  SyntaxEquivalentComparer.Instance)
          && GeneratorFactoryKey.Equals(other.GeneratorFactoryKey)
          && Diagnostics.SequenceEqual(other.Diagnostics);
    }

    public override int GetHashCode()
    {
      throw new UnreachableException();
    }
}
Enter fullscreen mode Exit fullscreen mode

This code fragment checks whether the this and other objects are equivalent. However, the developer made a mistake and compared the DiagnosticLocation property with itself.

Incorrect comparison:

DiagnosticLocation.Equals(DiagnosticLocation)
Enter fullscreen mode Exit fullscreen mode

Correct comparison:

DiagnosticLocation.Equals(other.DiagnosticLocation)
Enter fullscreen mode Exit fullscreen mode

I found this error in the LibraryImportGenerator class (link to GitHub). A bit later I found two more fragments — the same error, but in different classes:

Fun fact: .NET 7 has a test for this feature. However, the test is also incorrect, that's why it doesn't detect this error.

1015_NET7_Errors/image2.png

In .NET 8 the code is heavily rewritten. However, the developers haven't fixed the .NET 7 code yet — they decided to wait for the feedback. You can read more about it in the issue on GitHub.

Issue 2

internal static void CheckNullable(JSMarshalerType underlyingSig)
{
    MarshalerType underlying = underlyingSig._signatureType.Type;
    if (underlying == MarshalerType.Boolean
        || underlying == MarshalerType.Byte
        || underlying == MarshalerType.Int16
        || underlying == MarshalerType.Int32
        || underlying == MarshalerType.BigInt64
        || underlying == MarshalerType.Int52
        || underlying == MarshalerType.IntPtr
        || underlying == MarshalerType.Double
        || underlying == MarshalerType.Single // <=
        || underlying == MarshalerType.Single // <=
        || underlying == MarshalerType.Char
        || underlying == MarshalerType.DateTime
        || underlying == MarshalerType.DateTimeOffset
        ) return;
    throw new ArgumentException("Bad nullable value type");
}
Enter fullscreen mode Exit fullscreen mode

Location: JSMarshalerType.cs, 387 (link)

Here the developer double-checks if the underlying variable equals to MarshalerType.Single. Sometimes such checks hide errors: for example, the left and right variables should have been checked, but instead the left variable is checked twice. Here's a list of similar errors found in open-source projects.

I created an issue on GitHub: link. Luckily, this code fragment wasn't erroneous — it was just a redundant check.

Issue 3

public static bool TryParse(string text, out MetricSpec spec)
{
  int slashIdx = text.IndexOf(MeterInstrumentSeparator);
  if (slashIdx == -1)
  {
    spec = new MetricSpec(text.Trim(), null);
    return true;
  }
  else
  {
    string meterName = text.Substring(0, slashIdx).Trim();
    string? instrumentName = text.Substring(slashIdx + 1).Trim();
    spec = new MetricSpec(meterName, instrumentName);
    return true;
  }
}
Enter fullscreen mode Exit fullscreen mode

Location: MetricsEventSource.cs, 453 (link)

The TryParse method always returns true. This is weird. Let's see where this method is used:

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}");
      ....
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Location: MetricsEventSource.cs, 375 (link)

The return value of the TryParse method is used as the condition of the if statement. If specString cannot be parsed, the original value should be logged. Otherwise, the received representation (spec) is logged, and some operations are performed on it.

The problem is, TryParse always returns true. Thus, the then branch of the if statement is never executed — the parsing is always successful.

Issue on GitHub: link.

As a result of the fix, TryParse became Parse, and the caller method lost the if statement. The developers also changed Substring to AsSpan in TryParse.

1015_NET7_Errors/image3.png

By the way, this is the same code fragment that I noted when digging in the .NET 6 source code. But back then, the interpolation character was missing in the logging method:

if (!MetricSpec.TryParse(specString, out MetricSpec spec))
{
  Log.Message("Failed to parse metric spec: {specString}");
}
else
{
  Log.Message("Parsed metric: {spec}");
  ....
}
Enter fullscreen mode Exit fullscreen mode

You can read more about this issue in the article about .NET 6 check (issue 14).

Issue 4

Since we mentioned methods with strange return values, let's look at another one:

public virtual bool TryAdd(XmlDictionaryString value, out int key)
{
  ArgumentNullException.ThrowIfNull(value);

  IntArray? keys;

  if (_maps.TryGetValue(value.Dictionary, out keys))
  {
    key = (keys[value.Key] - 1);

    if (key != -1)
    {
      // If the key is already set, then something is wrong
      throw System.Runtime
                  .Serialization
                  .DiagnosticUtility
                  .ExceptionUtility
                  .ThrowHelperError(
      new InvalidOperationException(SR.XmlKeyAlreadyExists));
     }

     key = Add(value.Value);
     keys[value.Key] = (key + 1);
     return true;               // <=
  }

  key = Add(value.Value);
  keys = AddKeys(value.Dictionary, value.Key + 1);
  keys[value.Key] = (key + 1);
  return true;                  // <=
}
Enter fullscreen mode Exit fullscreen mode

Location: XmlBinaryWriterSession.cs, 28 (link)

The method either returns true or throws an exception — it never returns false. This is a public API, so there's more demand for quality.

Let's look at the description on learn.microsoft.com:

1015_NET7_Errors/image4.png

Oopsie. I created an issue on GitHub for it as well (link), but at the moment of writing this article there was no news on it.

Issue 5

public static Attribute? GetCustomAttribute(ParameterInfo element, 
                                            Type attributeType, 
                                            bool inherit)
{
  // ....
  Attribute[] attrib = GetCustomAttributes(element, attributeType, inherit);

  if (attrib == null || attrib.Length == 0)
    return null;

  if (attrib.Length == 0)
    return null;

  if (attrib.Length == 1)
    return attrib[0];

  throw new AmbiguousMatchException(SR.RFLCT_AmbigCust);
}
Enter fullscreen mode Exit fullscreen mode

Location: Attribute.CoreCLR.cs, 617 (link)

In this code fragment, the same expression — attrib.Length == 0 — is checked twice: first as a right operand of the '||' operator, then as a condition of the if statement.

Sometimes this may be an error — developers want to check one thing but instead check another. We were lucky here: the second check was just redundant and the developers removed it.

Issue on GitHub: link.

1015_NET7_Errors/image5.png

Issue 6

protected virtual XmlSchema? GetSchema()
{
  if (GetType() == typeof(DataTable))
  {
    return null;
  }
  MemoryStream stream = new MemoryStream();

  XmlWriter writer = new XmlTextWriter(stream, null);
  if (writer != null)
  {
    (new XmlTreeGen(SchemaFormat.WebService)).Save(this, writer);
  }
  stream.Position = 0;
  return XmlSchema.Read(new XmlTextReader(stream), null);
}
Enter fullscreen mode Exit fullscreen mode

Location: DataTable.cs, 6678 (link)

The developer created an instance of the XmlTextWriter type. Then a reference to this instance is assigned to the writer variable. However, in the next line the developer checked writer for null. The check always returns true, which means the condition is redundant here.

It's not horrific, but it's better to remove the check. The developers did that, actually (issue on GitHub).

1015_NET7_Errors/image6.png

Issue 7

Redundant code again, but this time it's less obvious:

public int ToFourDigitYear(int year, int twoDigitYearMax)
{
  if (year < 0)
  {
    throw new ArgumentOutOfRangeException(nameof(year), 
                                          SR.ArgumentOutOfRange_NeedPosNum);
  }

  if (year < 100)
  {
    int y = year % 100;
    return (twoDigitYearMax / 100 - (y > twoDigitYearMax % 100 ? 1 : 0)) 
             * 100 + y;
  }
  ....
}
Enter fullscreen mode Exit fullscreen mode

Location: GregorianCalendarHelper.cs, 526 (link)

Let's look at how the range of the year variable are checked throughout the code execution:

ToFourDigitYear(int year, int twoDigitYearMax)
Enter fullscreen mode Exit fullscreen mode

year is a parameter of the int type*.* Which means its value is within the [int.MinValue; int.MaxValue] range.

When the code is executed, the if statement is met first; in this statement, an exception is thrown:

if (year < 0)
{
  throw ....;
}
Enter fullscreen mode Exit fullscreen mode

If there's no exception, then the year value is within [0; int.MaxValue].

Then, another if statement:

if (year < 100)
{
  int y = year % 100;
  ....
}
Enter fullscreen mode Exit fullscreen mode

If the code execution is in the then branch of if, then the year value is within the [0; 99] range. This leads to an interesting result — to the operation of taking the remainder of the division:

int y = year % 100;
Enter fullscreen mode Exit fullscreen mode

The year value is always less than 100 (i.e., the value is between 0-99). Therefore, the result of the year % 100 operation is always equal to the left operand — year. Thus, y is always equal to year.

Either the code is redundant or it's an error. After I opened the issue on GitHub, the code was fixed and the y variable was removed.

Issue 8

internal ConfigurationSection
FindImmediateParentSection(ConfigurationSection section)
{
  ....
  SectionRecord sectionRecord = ....
  if (sectionRecord.HasLocationInputs)
  {
    SectionInput input = sectionRecord.LastLocationInput;
    Debug.Assert(input.HasResult, "input.HasResult");
    result = (ConfigurationSection)input.Result;
  }
  else
  {
    if (sectionRecord.HasIndirectLocationInputs)
    {
      Debug.Assert(IsLocationConfig, 
                   "Indirect location inputs exist 
                    only in location config record");
      SectionInput input = sectionRecord.LastIndirectLocationInput;
      Debug.Assert(input != null);
      Debug.Assert(input.HasResult, "input.HasResult");
      result = (ConfigurationSection)input.Result;
    }
    ....
  ....
}
Enter fullscreen mode Exit fullscreen mode

Location: MgmtConfigurationRecord.cs, 341 (link)

We need to dig a bit deeper here. First, let's look at the second if statement:

if (sectionRecord.HasIndirectLocationInputs)
{
  Debug.Assert(IsLocationConfig, 
               "Indirect location inputs exist 
                only in location config record");
  SectionInput input = sectionRecord.LastIndirectLocationInput;
  Debug.Assert(input != null);
  Debug.Assert(input.HasResult, "input.HasResult");
  result = (ConfigurationSection)input.Result;
}
Enter fullscreen mode Exit fullscreen mode

The value of the LastIndirectLocationInput property is written to the input variable*.* After that, input is checked in two asserts: it's checked for null (input != null) and for the presence of result (input.HasResult).

Let's look at the LastIndirectLocationInput property's body to understand which value can be written to the input variable:

internal SectionInput LastIndirectLocationInput
  =>   HasIndirectLocationInputs 
     ? IndirectLocationInputs[IndirectLocationInputs.Count - 1] 
     : null;
Enter fullscreen mode Exit fullscreen mode

On the one hand, the property may return null. On the other hand, if HasIndirectLocationInputs is true, then IndirectLocationInputs[IndirectLocationInputs.Count - 1] is returned instead of explicit null.

The question is, can the value from the IndirectLocationInputs collection be null? Probably yes, although it's not clear from the code. By the way, nullable annotations could help here, but they are not enabled in all .NET projects.

Let's go back to if:

if (sectionRecord.HasIndirectLocationInputs)
{
  Debug.Assert(IsLocationConfig, 
               "Indirect location inputs exist 
                only in location config record");
  SectionInput input = sectionRecord.LastIndirectLocationInput;
  Debug.Assert(input != null);
  Debug.Assert(input.HasResult, "input.HasResult");
  result = (ConfigurationSection)input.Result;
}
Enter fullscreen mode Exit fullscreen mode

The conditional expression is sectionRecord.HasIndirectLocationInputs. *It's the same property that's checked in *LastIndirectLocationInput. Which means LastIndirectLocationInput definitely doesn't return explicit null. However, it's unclear which value will be received from IndirectLocationInputs and written to input.

The developer first checks that input != null and only then checks for the presence of the result — input.HasResult. Looks okay.

Now let's go back to the first if statement:

if (sectionRecord.HasLocationInputs)
{
  SectionInput input = sectionRecord.LastLocationInput;
  Debug.Assert(input.HasResult, "input.HasResult");
  result = (ConfigurationSection)input.Result;
}
Enter fullscreen mode Exit fullscreen mode

Let's look at the LastLocationInput property:

internal SectionInput LastLocationInput 
  =>  HasLocationInputs 
    ? LocationInputs[LocationInputs.Count - 1] 
    : null;
Enter fullscreen mode Exit fullscreen mode

It's written the same way as LastIndirectLocationInput. Just like in the previous case, depending on the flag (HasLocationInputs), either null or a value from the LocationInputs collection is returned.

Now return to the if statement. Its conditional expression is the HasLocationInputs property, which is checked within LastLocationInput. If the code is executed in the then branch of the if statement, this means LastLocationInput cannot return explicit null. Can the value from the LocationInputs collection be null? The question remains unanswered. If it can, then null will be written to input too.

As in the case of the first inspected if, input.HasResult is checked but there's no input != null this time.

Once again. The first inspected code fragment:

SectionInput input = sectionRecord.LastIndirectLocationInput;
Debug.Assert(input != null);
Debug.Assert(input.HasResult, "input.HasResult");
result = (ConfigurationSection)input.Result;
Enter fullscreen mode Exit fullscreen mode

The second one:

SectionInput input = sectionRecord.LastLocationInput;
Debug.Assert(input.HasResult, "input.HasResult");
result = (ConfigurationSection)input.Result;
Enter fullscreen mode Exit fullscreen mode

Looks like the Debug.Assert(input != null) expression is missing.

I opened an issue on GitHub where I described this and other suspicious places related to null checks (you'll see them below).

The developers decided not to fix this fragment and left it as is:

1015_NET7_Errors/image7.png

Issues with null checks

I came across several places in code where a reference is dereferenced and only then it's checked for null. I created one issue for all similar code fragments on GitHub.

Let's inspect.

Issue 9

private static RuntimeBinderException BadOperatorTypesError(Expr pOperand1, 
                                                            Expr pOperand2)
{
  // ....
  string strOp = pOperand1.ErrorString;

  Debug.Assert(pOperand1 != null);
  Debug.Assert(pOperand1.Type != null);

  if (pOperand2 != null)
  {
    Debug.Assert(pOperand2.Type != null);
    return ErrorHandling.Error(ErrorCode.ERR_BadBinaryOps,
                               strOp, 
                               pOperand1.Type, 
                               pOperand2.Type);
  }

  return ErrorHandling.Error(ErrorCode.ERR_BadUnaryOp, strOp, pOperand1.Type);
}
Enter fullscreen mode Exit fullscreen mode

Location: ExpressionBinder.cs, 798 (link)

First, pOperand1 is dereferenced (pOperand1.ErrorString) and is checked for null in Debug.Assert *in the next code line. If *pOperand1 is null, then the assert is not triggered, but an exception of the NullReferenceException type is thrown instead.

The code was fixed — pOperand1 is checked before use.

Before:

string strOp = pOperand1.ErrorString;

Debug.Assert(pOperand1 != null);
Debug.Assert(pOperand1.Type != null);
Enter fullscreen mode Exit fullscreen mode

After:

Debug.Assert(pOperand1 != null);
Debug.Assert(pOperand1.Type != null);

string strOp = pOperand1.ErrorString;
Enter fullscreen mode Exit fullscreen mode

Issue 10

public void Execute()
{
  var count = _callbacks.Count;
  if (count == 0)
  {
    return;
  }

  List<Exception>? exceptions = null;

  if (_callbacks != null)
  {
    for (int i = 0; i < count; i++)
    {
      var callback = _callbacks[i];
      Execute(callback, ref exceptions);
    }
  }

  if (exceptions != null)
  {
    throw new AggregateException(exceptions);
  }
}
Enter fullscreen mode Exit fullscreen mode

Location: PipeCompletionCallbacks.cs, 20 (link)

The _callbacks variable is used first and only then it's checked for null:

public void Execute()
{
  var count = _callbacks.Count;
  ....
  if (_callbacks != null)
  ....
}
Enter fullscreen mode Exit fullscreen mode

At the time of writing this article, the developers removed checking _callbacks for null.

By the way, _callbacks is a readonly field that's initialized in a constructor:

internal sealed class PipeCompletionCallbacks
{
  private readonly List<PipeCompletionCallback> _callbacks;
  private readonly Exception? _exception;
  public PipeCompletionCallbacks(List<PipeCompletionCallback> callbacks, 
                                 ExceptionDispatchInfo? edi)
  {
    _callbacks = callbacks;
    _exception = edi?.SourceException;
  }
  ....
}
Enter fullscreen mode Exit fullscreen mode

In the thread with the fix, the developers discussed whether it was worth adding Debug.Assert and checking _callbacks for null into a constructor. In the end, they decided it wasn't.

1015_NET7_Errors/image8.png

Issue 11

private void ValidateAttributes(XmlElement elementNode)
{
  ....
  XmlSchemaAttribute schemaAttribute 
    = (_defaultAttributes[i] as XmlSchemaAttribute)!;
  attrQName = schemaAttribute.QualifiedName;
  Debug.Assert(schemaAttribute != null);
  ....
}
Enter fullscreen mode Exit fullscreen mode

Location: DocumentSchemaValidator.cs, 421 (link)

The controversial code:

  1. The result of the as operator is written to schemaAttribute. If _defaultAttributes[i]null or the cast failed, the result will be null.
  2. The null-forgiving operator ('!') implies that the result of casting cannot be null. Therefore, schemaAttribute cannot be null.
  3. In the next code line, schemaAttribute is dereferenced. Then in a line below, the reference is checked for null.

Here's the question. Can schemaAttribute be null? It's not very clear from the code.

The code was fixed like that:

....
XmlSchemaAttribute schemaAttribute 
  = (XmlSchemaAttribute)_defaultAttributes[i]!;
attrQName = schemaAttribute.QualifiedName;
....
Enter fullscreen mode Exit fullscreen mode

During the discussion of the fix, the developer proposed moving the Debug.Assert call in the line above instead of removing it. The code would look like that:

....
XmlSchemaAttribute schemaAttribute = (XmlSchemaAttribute)_defaultAttributes[i]!;
Debug.Assert(schemaAttribute != null);
attrQName = schemaAttribute.QualifiedName;
....
Enter fullscreen mode Exit fullscreen mode

In the end, they decided not to return Assert.

1015_NET7_Errors/image9.png

Issue 12

Let's look at the constructor of the* XmlConfigurationElementTextContent* type:

public XmlConfigurationElementTextContent(string textContent, 
                                          int? linePosition, 
                                          int? lineNumber)
{ .... }
Enter fullscreen mode Exit fullscreen mode

Location: XmlConfigurationElementTextContent.cs, 10 (link)

Now let's see where it's used:

public static IDictionary<string, string?> Read(....)
{
  ....
  case XmlNodeType.EndElement:
    ....
    var lineInfo = reader as IXmlLineInfo;
    var lineNumber = lineInfo?.LineNumber;
    var linePosition = lineInfo?.LinePosition;
    parent.TextContent = new XmlConfigurationElementTextContent(string.Empty, 
                                                                lineNumber,
                                                                linePosition);
    ....
    break;
  ....
  case XmlNodeType.Text:
    ....
    var lineInfo = reader as IXmlLineInfo;
    var lineNumber = lineInfo?.LineNumber;
    var linePosition = lineInfo?.LinePosition;

    XmlConfigurationElement parent = currentPath.Peek();

    parent.TextContent = new XmlConfigurationElementTextContent(reader.Value,
                                                                lineNumber, 
                                                                linePosition);
    ....
    break;
  ....
}
Enter fullscreen mode Exit fullscreen mode

Locations:

  • XmlStreamConfigurationProvider.cs, 133 (link)
  • XmlStreamConfigurationProvider.cs, 148 (link)

Have you noticed anything strange in code?

Pay attention to the order of arguments and parameters:

  • arguments: ..., lineNumber, linePosition;
  • parameters: ..., linePosition, lineNumber.

I created an issue on GitHub (link), the code was fixed: the developers put arguments in the correct order and added a test.

Issue 13

Another suspicious case:

public virtual bool Nested
{
  get {....}
  set 
  {
    ....
    ForeignKeyConstraint? constraint 
      = ChildTable.Constraints
                  .FindForeignKeyConstraint(ChildKey.ColumnsReference, 
                                            ParentKey.ColumnsReference); 
    ....
  }
}
Enter fullscreen mode Exit fullscreen mode

Location: DataRelation.cs, 486 (link)

Look at the* FindForeignKeyConstraint* method:

internal ForeignKeyConstraint? 
FindForeignKeyConstraint(DataColumn[] parentColumns, 
                         DataColumn[] childColumns)
{ .... }
Enter fullscreen mode Exit fullscreen mode

Location: ConstraintCollection.cs, 548 (link)

Seems like the argument order is mixed up again:

  • parameters: parent..., child...
  • arguments: ChildKey..., ParentKey...

There's another method call: the argument order is correct there.

ForeignKeyConstraint? foreignKey
  = relation.ChildTable
            .Constraints
            .FindForeignKeyConstraint(relation.ParentColumnsReference,
                                      relation.ChildColumnsReference);
Enter fullscreen mode Exit fullscreen mode

I created an issue on GitHub: link. Unfortunately, I haven't received any comments on it at the moment of writing the article.

Issue 14

These are not all places where the argument order is mixed up — I found another one:

void RecurseChildren(....)
{
  ....
  string? value 
    =  processValue != null
      ? processValue(new ConfigurationDebugViewContext(
                           child.Key, 
                           child.Path, 
                           valueAndProvider.Value, 
                           valueAndProvider.Provider))
      : valueAndProvider.Value;

  ....
}
Enter fullscreen mode Exit fullscreen mode

Location: ConfigurationRootExtensions.cs, 50 (link)

Look at the* ConfigurationDebugViewContext* constructor:

public ConfigurationDebugViewContext(
  string path, 
  string key, 
  string? value, 
  IConfigurationProvider configurationProvider) 
{ .... }
Enter fullscreen mode Exit fullscreen mode

Location: ConfigurationDebugViewContext.cs, 11 (link)

The order:

  • parameters: path, key, ...
  • arguments: child.Key, child.Path, ...

I created an issue on GitHub: link. According to the developers, this case doesn't have any issues despite the mistake.

1015_NET7_Errors/image10.png

However, they still fixed the order of arguments.

Conclusion

The .NET code is of high quality. I believe this is achieved by an established development process — the developers know the exact release date. Besides, release candidates help find the most serious errors and prepare the project for the release.

Nevertheless, I still manage to find something intriguing in the code. This time my favorites are arguments that were mixed up during method calls.

All code fragments described in this article were found by the PVS-Studio analyzer. Yes, now it can check projects on .NET 7.

If you want to check your projects (personal or commercial), download the analyzer here. There's also a link to the documentation on this page: we described how to enter the license and run the analysis. If you have any questions or issues — let us know and we'll help.

Latest comments (2)

Collapse
 
webjose profile image
José Pablo Ramírez Vargas

Hi. Very interesting. I have a comment, though, and it is just my very personal opinion.

I personally think this is too long for such a thick topic. Maybe this kind of thing could be made a series and release individual articles. I stopped reading at item 4. Still, interesting for sure. Cheers.

Collapse
 
_sergvasiliev_ profile image
Sergey Vasiliev

Hi,
Thank you for the comment! It's a reasonable opinion. I'll think of that idea for further publications. Although I'm not sure if it's possible to divide all articles into small parts. But anyway, thank you for the feedback. :)