rspec/rules/S3900/csharp/rule.adoc

108 lines
2.8 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
Methods declared as `public`, `protected`, or `protected internal` can be accessed from other assemblies, which means you should validate parameters to be within the expected constraints. In general, checking against `null` is recommended in defensive programming.
2021-04-28 16:49:39 +02:00
This rule raises an issue when a parameter of a publicly accessible method is not validated against `null` before being dereferenced.
2021-04-28 16:49:39 +02:00
=== Noncompliant code example
2021-04-28 16:49:39 +02:00
2022-02-04 17:28:24 +01:00
[source,csharp]
2021-04-28 16:49:39 +02:00
----
public class MyClass
{
private MyOtherClass other;
2023-04-13 11:50:57 +02:00
public void Foo(MyOtherClass other)
2021-04-28 16:49:39 +02:00
{
this.other = other.Clone(); // Noncompliant
}
protected void Bar(MyOtherClass other)
2021-04-28 16:49:39 +02:00
{
this.other = other.Clone(); // Noncompliant
}
}
----
=== Compliant solution
2021-04-28 16:49:39 +02:00
2022-02-04 17:28:24 +01:00
[source,csharp]
2021-04-28 16:49:39 +02:00
----
public class MyClass
{
private MyOtherClass other;
2023-04-13 11:50:57 +02:00
public void Foo(MyOtherClass other)
2021-04-28 16:49:39 +02:00
{
2023-04-13 11:50:57 +02:00
if (other != null)
2021-04-28 16:49:39 +02:00
{
this.other = other.Clone();
}
}
2023-04-13 11:50:57 +02:00
protected void Bar(MyOtherClass other)
2021-04-28 16:49:39 +02:00
{
2023-04-13 11:50:57 +02:00
if (other != null)
2021-04-28 16:49:39 +02:00
{
this.other = other.Clone();
}
}
2023-04-13 11:50:57 +02:00
public void Baz(MyOtherClass other)
{
ArgumentNullException.ThrowIfNull(other);
this.other = other.Clone();
}
2023-04-13 11:50:57 +02:00
public void Qux(MyOtherClass other)
{
this.other = other; // Compliant: "other" is not being dereferenced
}
2023-04-13 11:50:57 +02:00
private void Xyzzy(MyOtherClass other)
{
this.other = other.Clone(); // Compliant: method is not publicly accessible
}
2021-04-28 16:49:39 +02:00
}
----
=== Exceptions
* Arguments validated for `null` via helper methods should be annotated with the https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis#postconditions-maybenull-and-notnull[`[NotNull]`] attribute.
* Method parameters marked with the `[NotNull]` https://www.jetbrains.com/help/resharper/Reference__Code_Annotation_Attributes.html#ItemNotNullAttribute[Resharper code annotation attribute] are supported as well.
* To create a custom null validation method declare an attribute with name `ValidatedNotNullAttribute` and mark the parameter that is validated for null in your method declaration with it:
2021-04-28 16:49:39 +02:00
[source,csharp]
2021-04-28 16:49:39 +02:00
----
using System;
[AttributeUsage(AttributeTargets.Parameter, Inherited=false)]
2021-04-28 16:49:39 +02:00
public sealed class ValidatedNotNullAttribute : Attribute { }
public static class Guard
{
public static void NotNull<T>([ValidatedNotNullAttribute] T value, [CallerArgumentExpression("value")] string name = "") where T : class
2021-04-28 16:49:39 +02:00
{
if (value == null)
throw new ArgumentNullException(name);
}
}
public static class Utils
{
public static string ToUpper(string value)
{
Guard.NotNull(value);
2021-04-28 16:49:39 +02:00
return value.ToUpper(); // Compliant
}
}
----
include::../rspecator.adoc[]