Skip to content

Address some stuff reported/suggested by code analyzers #167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_style_prefer_auto_properties = true : suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true : suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true : silent
dotnet_style_prefer_conditional_expression_over_return = true : silent
dotnet_style_prefer_conditional_expression_over_return = true

dotnet_style_coalesce_expression = true : warning
dotnet_style_null_propagation = true : warning
Expand Down Expand Up @@ -176,5 +176,36 @@ dotnet_naming_rule.everything_else_naming.style = camel_case_
dotnet_naming_symbols.everything_else.applicable_kinds = *
dotnet_naming_symbols.everything_else.applicable_accessibilities = *

# CA1040: Avoid empty interfaces
dotnet_diagnostic.CA1040.severity = none

# CA1812: Avoid uninstantiated internal classes
dotnet_diagnostic.CA1812.severity = none

# CA1724: Type names should not match namespaces
dotnet_diagnostic.CA1724.severity = none

# CA1819: Properties should not return arrays
dotnet_diagnostic.CA1819.severity = none

# CA2225: Provide a method … as an alternate for operator
dotnet_diagnostic.CA2225.severity = none

# IDE0022: Use expression body for methods
dotnet_diagnostic.IDE0022.severity = suggestion

# IDE0046: Convert to conditional expression
dotnet_diagnostic.IDE0046.severity = suggestion

# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = none

# IDE0058: Remove unnecessary expression value
dotnet_diagnostic.IDE0058.severity = none

# IDE0160: Convert to block scoped namespace
csharp_style_namespace_declarations = file_scoped

# IDE0340: Use unbound generic type
# To be revised once we are on .NET 10 SDK
csharp_style_prefer_unbound_generic_type_in_nameof = false
1 change: 1 addition & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<PackageReference Update="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="$(CscVer)" />
<PackageReference Update="Microsoft.Extensions.DependencyModel" Version="$(ExtsVer)" />
<PackageReference Update="Microsoft.Extensions.FileSystemGlobbing" Version="$(ExtsVer)" />
<PackageReference Update="System.Collections.Immutable" Version="9.0.0" />
<PackageReference Update="System.Reflection.MetadataLoadContext" Version="$(ExtsVer)" />
<PackageReference Include="LeanCode.CodeAnalysis" Version="$(LncdCaVer)" PrivateAssets="all" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions examples/notifications/generic.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#pragma warning disable IDE0005
using System;
#pragma warning restore IDE0005
using LeanCode.Contracts;

namespace Notifications.Generic;
Expand Down
82 changes: 35 additions & 47 deletions src/LeanCode.Contracts.Admin/AdminQuery.cs
Original file line number Diff line number Diff line change
@@ -1,47 +1,35 @@
namespace LeanCode.Contracts.Admin;

public abstract class AdminQuery<TResult> : IQuery<AdminQueryResult<TResult>>
{
/// <remarks>0-based</remarks>
public int Page { get; set; }
public int PageSize { get; set; }

public bool? SortDescending { get; set; }
public string? SortBy { get; set; }
}

public class AdminQueryResult<TResult>
{
public long Total { get; set; }
public List<TResult> Items { get; set; }
}

public class AdminFilterRange<T>
{
public T? From { get; set; }
public T? To { get; set; }
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class AdminFilterFor : Attribute
{
public AdminFilterFor(string name) { }
}

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class AdminLabel : Attribute
{
public AdminLabel(string label) { }
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class AdminColumn : Attribute
{
public AdminColumn(string? name) { }
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class AdminSortable : Attribute
{
public AdminSortable() { }
}
namespace LeanCode.Contracts.Admin;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know what happened in this file? Encoding change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed line endings from CRLF to LF.

public abstract class AdminQuery<TResult> : IQuery<AdminQueryResult<TResult>>
{
/// <remarks>0-based</remarks>
public int Page { get; set; }
public int PageSize { get; set; }

public bool? SortDescending { get; set; }
public string? SortBy { get; set; }
}

public class AdminQueryResult<TResult>
{
public long Total { get; set; }
public List<TResult> Items { get; set; }
}

public class AdminFilterRange<T>
{
public T? From { get; set; }
public T? To { get; set; }
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class AdminFilterForAttribute(string name) : Attribute;

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class AdminLabelAttribute(string label) : Attribute;

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class AdminColumnAttribute(string? name) : Attribute;

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class AdminSortableAttribute : Attribute;
35 changes: 19 additions & 16 deletions src/LeanCode.Contracts/Binary.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
using System.Diagnostics.CodeAnalysis;

namespace LeanCode.Contracts;

[System.Text.Json.Serialization.JsonConverter(typeof(Converters.BinaryJsonConverter))]
public record struct Binary : IEquatable<Binary>
public readonly record struct Binary : IEquatable<Binary>
{
private readonly byte[]? data;

public byte[] Data => data ?? Array.Empty<byte>();
public readonly byte[] Data => data ?? [];

public Binary(byte[] data)
public Binary(byte[]? data)
{
ArgumentNullException.ThrowIfNull(data);

this.data = data;
}

public bool Equals(Binary other)
{
var otherData = other.Data;
return Data.Length == otherData.Length && Data.SequenceEqual(otherData);
}
public bool Equals(Binary other) => MemoryExtensions.SequenceEqual(Data, other.Data);

public override int GetHashCode() => Data.Length;
public override int GetHashCode() =>
data switch
{
{ LongLength: > 4 } d => HashCode.Combine(
BitConverter.ToInt32(d.AsSpan(0..4)),
BitConverter.ToInt32(d.AsSpan(^4..^0))
),
Comment on lines +22 to +25
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you let me know how that works, especially the second call to ToInt32? Does it somehow generate int32 from the array of arbitrary length?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each call reinterprets 4 bytes of the array as int32, the first one looking at first 4 bytes and the second looking at last 4 bytes. It's not by any means smart but it's as much input data as I can get in fixed time as reasonably possible.

{ LongLength: 4 } d => BitConverter.ToInt32(d),
{ LongLength: 2 or 3 } d => BitConverter.ToInt16(d),
{ LongLength: 1 } d => d[0],
_ => 0,
};

#if NET7_0_OR_GREATER
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull(nameof(binary))]
#else
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("binary")]
#endif
[return: NotNullIfNotNull(nameof(binary))]
public static implicit operator byte[]?(Binary? binary) => binary?.Data;
}
11 changes: 3 additions & 8 deletions src/LeanCode.Contracts/CommandResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,12 @@

namespace LeanCode.Contracts;

public sealed class CommandResult
public sealed class CommandResult(ImmutableList<ValidationError> validationErrors)
{
public ImmutableList<ValidationError> ValidationErrors { get; }
public ImmutableList<ValidationError> ValidationErrors { get; } = validationErrors;
public bool WasSuccessful => ValidationErrors.Count == 0;

public CommandResult(ImmutableList<ValidationError> validationErrors)
{
ValidationErrors = validationErrors;
}

public static CommandResult Success { get; } = new(ImmutableList.Create<ValidationError>());
public static CommandResult Success { get; } = new([]);

public static CommandResult NotValid(ValidationResult validationResult)
{
Expand Down
2 changes: 1 addition & 1 deletion src/LeanCode.Contracts/Converters/BinaryJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace LeanCode.Contracts.Converters;

internal class BinaryJsonConverter : JsonConverter<Binary>
internal sealed class BinaryJsonConverter : JsonConverter<Binary>
{
public override bool HandleNull => false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ namespace LeanCode.Contracts;
AllowMultiple = false,
Inherited = false
)]
public sealed class ExcludeFromContractsGenerationAttribute : Attribute { }
public sealed class ExcludeFromContractsGenerationAttribute : Attribute;
3 changes: 1 addition & 2 deletions src/LeanCode.Contracts/ICommand.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
namespace LeanCode.Contracts;

[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")]
public interface ICommand { }
public interface ICommand;
6 changes: 2 additions & 4 deletions src/LeanCode.Contracts/IOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ namespace LeanCode.Contracts;
/// <summary>
/// Marker interface, do not use directly.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")]
public interface IOperation { }
public interface IOperation;

[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")]
public interface IOperation<out TResult> : IOperation { }
public interface IOperation<out TResult> : IOperation;
3 changes: 1 addition & 2 deletions src/LeanCode.Contracts/IProduceNotification.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
namespace LeanCode.Contracts;

[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")]
public interface IProduceNotification<TNotification>
where TNotification : notnull { }
where TNotification : notnull;
6 changes: 2 additions & 4 deletions src/LeanCode.Contracts/IQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ namespace LeanCode.Contracts;
/// <summary>
/// Marker interface, do not use directly.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")]
public interface IQuery { }
public interface IQuery;

[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")]
public interface IQuery<out TResult> : IQuery { }
public interface IQuery<out TResult> : IQuery;
3 changes: 1 addition & 2 deletions src/LeanCode.Contracts/ITopic.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
namespace LeanCode.Contracts;

[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")]
public interface ITopic { }
public interface ITopic;
5 changes: 1 addition & 4 deletions src/LeanCode.Contracts/NotificationEnvelope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ public NotificationEnvelope(Guid id, ITopic topic, object notification)

public static NotificationEnvelope Create<TTopic, TNotification>(TTopic topic, TNotification notification)
where TTopic : ITopic, IProduceNotification<TNotification>
where TNotification : notnull
{
return new(Guid.NewGuid(), topic, notification);
}
where TNotification : notnull => new(Guid.NewGuid(), topic, notification);

[JsonConstructor]
public NotificationEnvelope(Guid id, string topicType, string notificationType, object topic, object notification)
Expand Down
42 changes: 7 additions & 35 deletions src/LeanCode.Contracts/NotificationTagGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Text;

namespace LeanCode.Contracts;

public static class NotificationTagGenerator
Expand All @@ -12,22 +10,20 @@ public static class NotificationTagGenerator
/// `LeanCode.ContractsGenerator.Generation.NotificationTagGenerator` that generates a tag based on `TypeRef`.
/// Both methods generate the same tags.
/// </summary>
public static string Generate(Type type)
{
return type switch
public static string Generate(Type type) =>
type switch
{
_ when TryKnownType(type) is string name => type.GetElementType() is Type t
? $"{KnownTypePrefix}{name}{GetArgumentsString(t)}"
: $"{KnownTypePrefix}{name}{GetArgumentsString(type.GetGenericArguments())}",
_ when type.IsGenericType => $"{type.GetSimpleName()}{GetArgumentsString(type.GetGenericArguments())}",
_ => type.FullName!,
};
}

private static string GetSimpleName(this Type type)
{
var typeName = type.FullName!;
var backtickIndex = typeName.IndexOf('`');
var backtickIndex = typeName.IndexOf('`', StringComparison.Ordinal);

if (backtickIndex > 0)
{
Expand All @@ -37,34 +33,11 @@ private static string GetSimpleName(this Type type)
return typeName;
}

private static string GetArgumentsString(params Type[] args)
{
var argsBuilder = new StringBuilder();

if (args.Any())
{
argsBuilder.Append('[');

foreach (var arg in args)
{
if (argsBuilder.Length > 1)
{
argsBuilder.Append(',');
}
private static string GetArgumentsString(params Type[] args) =>
args.Length > 0 ? $"[{string.Join(',', args.Select(Generate))}]" : string.Empty;

var argName = Generate(arg);
argsBuilder.Append(argName);
}

argsBuilder.Append(']');
}

return argsBuilder.ToString();
}

private static string? TryKnownType(Type type)
{
return type switch
private static string? TryKnownType(Type type) =>
type switch
{
_ when type == typeof(object) => "Object",
_ when type == typeof(string) => "String",
Expand Down Expand Up @@ -111,5 +84,4 @@ when type.IsGenericType
_ when type.IsArray => "Array",
_ => null,
};
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace LeanCode.Contracts.Security;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
public sealed class AllowUnauthorizedAttribute : Attribute { }
public sealed class AllowUnauthorizedAttribute : Attribute;
Loading
Loading