The following piece of code results in an AD0001:
/// <summary>
/// Provides a base implementation for JSON converters of structs that may or may not be nullable.
/// </summary>
/// <typeparam name="T">The type of structs to convert.</typeparam>
[Inheritable]
public class NullableStructJsonConverter<T> : JsonConverterFactory
where T : struct
{
private static readonly JsonSerializerOptions FallbackOptions = new(JsonSerializerDefaults.Web);
private readonly Nullable nullable;
private readonly NotNullable notNullable;
/// <summary>
/// Initializes a new instance of the <see cref="NullableStructJsonConverter{T}"/> class.
/// </summary>
protected NullableStructJsonConverter()
{
nullable = new(this);
notNullable = new(this);
}
/// <inheritdoc />
[Pure]
public override bool CanConvert(Type typeToConvert)
=> typeToConvert == typeof(T) || typeToConvert == typeof(T?);
/// <inheritdoc />
[Pure]
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) => this switch
{
_ when typeToConvert == typeof(T) => notNullable,
_ when typeToConvert == typeof(T?) => nullable,
_ => null,
};
/// <inheritdoc cref="JsonConverter{T}.Read(ref Utf8JsonReader, Type, JsonSerializerOptions)" />
[Pure]
public virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> ReadNullable(ref reader, typeToConvert, options) ?? default;
/// <inheritdoc cref="Read(ref Utf8JsonReader, Type, JsonSerializerOptions)" />
[Pure]
public virtual T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> JsonSerializer.Deserialize<T?>(ref reader, FallbackOptions);
/// <inheritdoc cref="JsonConverter{T}.Write(Utf8JsonWriter, T, JsonSerializerOptions)" />
public virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
=> JsonSerializer.Serialize(writer, value, FallbackOptions);
/// <inheritdoc cref="Write(Utf8JsonWriter, T, JsonSerializerOptions)" />
public virtual void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
=> JsonSerializer.Serialize(writer, value, FallbackOptions);
private sealed class NotNullable(NullableStructJsonConverter<T> converter) : JsonConverter<T>
{
/// <inheritdoc />
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> converter.Read(ref reader, typeToConvert, options);
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
=> converter.Write(writer, value, FallbackOptions);
}
private sealed class Nullable(NullableStructJsonConverter<T> converter) : JsonConverter<T?>
{
/// <inheritdoc />
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> converter.ReadNullable(ref reader, typeToConvert, options);
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
=> converter.Write(writer, value, FallbackOptions);
}
}
With an detailed error:
Analyzer 'SonarAnalyzer.CSharp.Rules.UnusedPrivateMember' threw an exception of type 'System.IndexOutOfRangeException' with message 'Index was outside the bounds of the array.'.
Exception occurred with following context:
Compilation: *****
ISymbol: NullableStructJsonConverter (NamedType)
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Collections.Immutable.ImmutableArray`1.get_Item(Int32 index)
at SonarAnalyzer.CSharp.Core.Wrappers.ObjectCreationFactory.ImplicitObjectCreation.TypeAsString(SemanticModel semanticModel)
at SonarAnalyzer.CSharp.Syntax.Utilities.SymbolUsageCollector.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
at SonarAnalyzer.CSharp.Syntax.Utilities.SymbolUsageCollector.VisitAssignmentExpression(AssignmentExpressionSyntax node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
at SonarAnalyzer.CSharp.Syntax.Utilities.SymbolUsageCollector.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
at SonarAnalyzer.CSharp.Syntax.Utilities.SymbolUsageCollector.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
at SonarAnalyzer.CSharp.Syntax.Utilities.SymbolUsageCollector.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
at SonarAnalyzer.CSharp.Syntax.Utilities.SymbolUsageCollector.VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
at SonarAnalyzer.CSharp.Syntax.Utilities.SymbolUsageCollector.Visit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)
at SonarAnalyzer.CSharp.Syntax.Utilities.SymbolUsageCollector.Visit(SyntaxNode node)
at SonarAnalyzer.CSharp.Core.Syntax.Utilities.SafeCSharpSyntaxWalker.SafeVisit(SyntaxNode syntaxNode)
at SonarAnalyzer.CSharp.Rules.UnusedPrivateMember.<>c__DisplayClass28_0.<VisitDeclaringReferences>b__1(SyntaxReference x)
at System.Linq.Enumerable.All[TSource](IEnumerable`1 source, Func`2 predicate)
at SonarAnalyzer.CSharp.Rules.UnusedPrivateMember.VisitDeclaringReferences(ISymbol symbol, ISafeSyntaxWalker visitor, SonarSymbolReportingContext context, Boolean includeGeneratedFile)
at SonarAnalyzer.CSharp.Rules.UnusedPrivateMember.NamedSymbolAction(SonarSymbolReportingContext context, HashSet`1 removableInternalTypes)
at SonarAnalyzer.CSharp.Rules.UnusedPrivateMember.<>c__DisplayClass14_0.<Initialize>b__1(SonarSymbolReportingContext x)
at SonarAnalyzer.Core.AnalysisContext.SonarCompilationStartAnalysisContext.<>c__DisplayClass11_0.<RegisterSymbolAction>b__0(SymbolAnalysisContext x)
at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info, CancellationToken cancellationToken)
-----
Suppress the following diagnostics to disable this analyzer: S1144, S4487
Reproducable with Release 10.23 · SonarSource/sonar-dotnet · GitHub