diff --git a/.editorconfig b/.editorconfig index 2eb3de9..10ae800 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 @@ -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 diff --git a/Directory.Build.targets b/Directory.Build.targets index a442ed5..945c4de 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -24,6 +24,7 @@ + diff --git a/examples/notifications/generic.cs b/examples/notifications/generic.cs index 920b58a..4423a66 100644 --- a/examples/notifications/generic.cs +++ b/examples/notifications/generic.cs @@ -1,4 +1,6 @@ +#pragma warning disable IDE0005 using System; +#pragma warning restore IDE0005 using LeanCode.Contracts; namespace Notifications.Generic; diff --git a/src/LeanCode.Contracts.Admin/AdminQuery.cs b/src/LeanCode.Contracts.Admin/AdminQuery.cs index f939b99..4e266ca 100644 --- a/src/LeanCode.Contracts.Admin/AdminQuery.cs +++ b/src/LeanCode.Contracts.Admin/AdminQuery.cs @@ -1,47 +1,35 @@ -namespace LeanCode.Contracts.Admin; - -public abstract class AdminQuery : IQuery> -{ - /// 0-based - public int Page { get; set; } - public int PageSize { get; set; } - - public bool? SortDescending { get; set; } - public string? SortBy { get; set; } -} - -public class AdminQueryResult -{ - public long Total { get; set; } - public List Items { get; set; } -} - -public class AdminFilterRange -{ - 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; + +public abstract class AdminQuery : IQuery> +{ + /// 0-based + public int Page { get; set; } + public int PageSize { get; set; } + + public bool? SortDescending { get; set; } + public string? SortBy { get; set; } +} + +public class AdminQueryResult +{ + public long Total { get; set; } + public List Items { get; set; } +} + +public class AdminFilterRange +{ + 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; diff --git a/src/LeanCode.Contracts/Binary.cs b/src/LeanCode.Contracts/Binary.cs index 3e6cb0d..e2b4f6f 100644 --- a/src/LeanCode.Contracts/Binary.cs +++ b/src/LeanCode.Contracts/Binary.cs @@ -1,31 +1,34 @@ +using System.Diagnostics.CodeAnalysis; + namespace LeanCode.Contracts; [System.Text.Json.Serialization.JsonConverter(typeof(Converters.BinaryJsonConverter))] -public record struct Binary : IEquatable +public readonly record struct Binary : IEquatable { private readonly byte[]? data; - public byte[] Data => data ?? Array.Empty(); + 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)) + ), + { 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; } diff --git a/src/LeanCode.Contracts/CommandResult.cs b/src/LeanCode.Contracts/CommandResult.cs index ab301fe..282321d 100644 --- a/src/LeanCode.Contracts/CommandResult.cs +++ b/src/LeanCode.Contracts/CommandResult.cs @@ -3,17 +3,12 @@ namespace LeanCode.Contracts; -public sealed class CommandResult +public sealed class CommandResult(ImmutableList validationErrors) { - public ImmutableList ValidationErrors { get; } + public ImmutableList ValidationErrors { get; } = validationErrors; public bool WasSuccessful => ValidationErrors.Count == 0; - public CommandResult(ImmutableList validationErrors) - { - ValidationErrors = validationErrors; - } - - public static CommandResult Success { get; } = new(ImmutableList.Create()); + public static CommandResult Success { get; } = new([]); public static CommandResult NotValid(ValidationResult validationResult) { diff --git a/src/LeanCode.Contracts/Converters/BinaryJsonConverter.cs b/src/LeanCode.Contracts/Converters/BinaryJsonConverter.cs index 3d0e14e..a1becf7 100644 --- a/src/LeanCode.Contracts/Converters/BinaryJsonConverter.cs +++ b/src/LeanCode.Contracts/Converters/BinaryJsonConverter.cs @@ -3,7 +3,7 @@ namespace LeanCode.Contracts.Converters; -internal class BinaryJsonConverter : JsonConverter +internal sealed class BinaryJsonConverter : JsonConverter { public override bool HandleNull => false; diff --git a/src/LeanCode.Contracts/ExcludeFromContractsGenerationAttribute.cs b/src/LeanCode.Contracts/ExcludeFromContractsGenerationAttribute.cs index 2974dd7..338ce4b 100644 --- a/src/LeanCode.Contracts/ExcludeFromContractsGenerationAttribute.cs +++ b/src/LeanCode.Contracts/ExcludeFromContractsGenerationAttribute.cs @@ -7,4 +7,4 @@ namespace LeanCode.Contracts; AllowMultiple = false, Inherited = false )] -public sealed class ExcludeFromContractsGenerationAttribute : Attribute { } +public sealed class ExcludeFromContractsGenerationAttribute : Attribute; diff --git a/src/LeanCode.Contracts/ICommand.cs b/src/LeanCode.Contracts/ICommand.cs index 6ace3ef..d1e6bc8 100644 --- a/src/LeanCode.Contracts/ICommand.cs +++ b/src/LeanCode.Contracts/ICommand.cs @@ -1,4 +1,3 @@ namespace LeanCode.Contracts; -[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")] -public interface ICommand { } +public interface ICommand; diff --git a/src/LeanCode.Contracts/IOperation.cs b/src/LeanCode.Contracts/IOperation.cs index 87cd347..ed4adc2 100644 --- a/src/LeanCode.Contracts/IOperation.cs +++ b/src/LeanCode.Contracts/IOperation.cs @@ -3,8 +3,6 @@ namespace LeanCode.Contracts; /// /// Marker interface, do not use directly. /// -[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 : IOperation { } +public interface IOperation : IOperation; diff --git a/src/LeanCode.Contracts/IProduceNotification.cs b/src/LeanCode.Contracts/IProduceNotification.cs index 3a85417..992e734 100644 --- a/src/LeanCode.Contracts/IProduceNotification.cs +++ b/src/LeanCode.Contracts/IProduceNotification.cs @@ -1,5 +1,4 @@ namespace LeanCode.Contracts; -[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")] public interface IProduceNotification - where TNotification : notnull { } + where TNotification : notnull; diff --git a/src/LeanCode.Contracts/IQuery.cs b/src/LeanCode.Contracts/IQuery.cs index 96816c1..63519fb 100644 --- a/src/LeanCode.Contracts/IQuery.cs +++ b/src/LeanCode.Contracts/IQuery.cs @@ -3,8 +3,6 @@ namespace LeanCode.Contracts; /// /// Marker interface, do not use directly. /// -[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 : IQuery { } +public interface IQuery : IQuery; diff --git a/src/LeanCode.Contracts/ITopic.cs b/src/LeanCode.Contracts/ITopic.cs index c2229db..1c94cab 100644 --- a/src/LeanCode.Contracts/ITopic.cs +++ b/src/LeanCode.Contracts/ITopic.cs @@ -1,4 +1,3 @@ namespace LeanCode.Contracts; -[System.Diagnostics.CodeAnalysis.SuppressMessage("?", "CA1040", Justification = "Marker interface.")] -public interface ITopic { } +public interface ITopic; diff --git a/src/LeanCode.Contracts/NotificationEnvelope.cs b/src/LeanCode.Contracts/NotificationEnvelope.cs index 1ea3718..b6775e3 100644 --- a/src/LeanCode.Contracts/NotificationEnvelope.cs +++ b/src/LeanCode.Contracts/NotificationEnvelope.cs @@ -21,10 +21,7 @@ public NotificationEnvelope(Guid id, ITopic topic, object notification) public static NotificationEnvelope Create(TTopic topic, TNotification notification) where TTopic : ITopic, IProduceNotification - 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) diff --git a/src/LeanCode.Contracts/NotificationTagGenerator.cs b/src/LeanCode.Contracts/NotificationTagGenerator.cs index 126a529..9d3984d 100644 --- a/src/LeanCode.Contracts/NotificationTagGenerator.cs +++ b/src/LeanCode.Contracts/NotificationTagGenerator.cs @@ -1,5 +1,3 @@ -using System.Text; - namespace LeanCode.Contracts; public static class NotificationTagGenerator @@ -12,9 +10,8 @@ public static class NotificationTagGenerator /// `LeanCode.ContractsGenerator.Generation.NotificationTagGenerator` that generates a tag based on `TypeRef`. /// Both methods generate the same tags. /// - 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)}" @@ -22,12 +19,11 @@ _ when TryKnownType(type) is string name => type.GetElementType() is Type t _ 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) { @@ -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", @@ -111,5 +84,4 @@ when type.IsGenericType _ when type.IsArray => "Array", _ => null, }; - } } diff --git a/src/LeanCode.Contracts/Security/AllowUnauthorizedAttribute.cs b/src/LeanCode.Contracts/Security/AllowUnauthorizedAttribute.cs index 4bc0a13..8a2736a 100644 --- a/src/LeanCode.Contracts/Security/AllowUnauthorizedAttribute.cs +++ b/src/LeanCode.Contracts/Security/AllowUnauthorizedAttribute.cs @@ -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; diff --git a/src/LeanCode.Contracts/Security/AuthorizeWhenAttribute.cs b/src/LeanCode.Contracts/Security/AuthorizeWhenAttribute.cs index 8b60fa5..d7a6571 100644 --- a/src/LeanCode.Contracts/Security/AuthorizeWhenAttribute.cs +++ b/src/LeanCode.Contracts/Security/AuthorizeWhenAttribute.cs @@ -4,23 +4,15 @@ namespace LeanCode.Contracts.Security; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)] -public abstract class AuthorizeWhenAttribute : Attribute +public abstract class AuthorizeWhenAttribute(Type authorizerType, object? customData = null) : Attribute { - private readonly Type authorizerType; - private readonly object? customData; - - protected AuthorizeWhenAttribute(Type authorizerType, object? customData = null) - { - this.authorizerType = authorizerType; - this.customData = customData; - } + private readonly Type authorizerType = authorizerType; + private readonly object? customData = customData; public static List GetCustomAuthorizers() => GetCustomAuthorizers(typeof(T)); - public static List GetCustomAuthorizers(Type type) - { - return type.GetCustomAttributes().Select(AuthorizerDefinition.Create).ToList(); - } + public static List GetCustomAuthorizers(Type type) => + [.. type.GetCustomAttributes().Select(AuthorizerDefinition.Create)]; [SuppressMessage("?", "CA1034", Justification = "Deliberate nesting.")] public sealed class AuthorizerDefinition @@ -39,11 +31,9 @@ private AuthorizerDefinition(AuthorizeWhenAttribute attr) } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = true)] -public abstract class AuthorizeWhenAttribute : AuthorizeWhenAttribute +public abstract class AuthorizeWhenAttribute(object? customData = null) + : AuthorizeWhenAttribute(typeof(T), customData) { - protected AuthorizeWhenAttribute(object? customData = null) - : base(typeof(T), customData) { } - [SuppressMessage("?", "CA1000", Justification = "Alternative method also exists.")] - public static List GetCustomAuthorizers() => GetCustomAuthorizers(typeof(T)); + public static List GetCustomAuthorizers() => GetCustomAuthorizers(); } diff --git a/src/LeanCode.Contracts/Security/AuthorizeWhenHasAnyOfAttribute.cs b/src/LeanCode.Contracts/Security/AuthorizeWhenHasAnyOfAttribute.cs index a90ab64..52205af 100644 --- a/src/LeanCode.Contracts/Security/AuthorizeWhenHasAnyOfAttribute.cs +++ b/src/LeanCode.Contracts/Security/AuthorizeWhenHasAnyOfAttribute.cs @@ -1,12 +1,6 @@ -using System.Diagnostics.CodeAnalysis; - namespace LeanCode.Contracts.Security; -[SuppressMessage("?", "CA1040", Justification = "Marker interface.")] -public interface IHasPermissions { } +public interface IHasPermissions; -public sealed class AuthorizeWhenHasAnyOfAttribute : AuthorizeWhenAttribute -{ - public AuthorizeWhenHasAnyOfAttribute(params string[] permissions) - : base(typeof(IHasPermissions), permissions) { } -} +public sealed class AuthorizeWhenHasAnyOfAttribute(params string[] permissions) + : AuthorizeWhenAttribute(typeof(IHasPermissions), permissions); diff --git a/src/LeanCode.Contracts/SubscriptionEnvelope.cs b/src/LeanCode.Contracts/SubscriptionEnvelope.cs index cbba4bd..77a613f 100644 --- a/src/LeanCode.Contracts/SubscriptionEnvelope.cs +++ b/src/LeanCode.Contracts/SubscriptionEnvelope.cs @@ -2,9 +2,11 @@ namespace LeanCode.Contracts; -public sealed class SubscriptionEnvelope +public sealed class SubscriptionEnvelope : IDisposable { public Guid Id { get; set; } public string TopicType { get; set; } = default!; public JsonDocument Topic { get; set; } = default!; + + public void Dispose() => Topic.Dispose(); } diff --git a/src/LeanCode.Contracts/SubscriptionResult.cs b/src/LeanCode.Contracts/SubscriptionResult.cs index b57726c..dce1b58 100644 --- a/src/LeanCode.Contracts/SubscriptionResult.cs +++ b/src/LeanCode.Contracts/SubscriptionResult.cs @@ -1,17 +1,10 @@ namespace LeanCode.Contracts; -public sealed class SubscriptionResult +public sealed class SubscriptionResult(Guid subscriptionId, SubscriptionStatus status, OperationType type) { - public Guid SubscriptionId { get; private init; } - public SubscriptionStatus Status { get; private init; } - public OperationType Type { get; private init; } - - public SubscriptionResult(Guid subscriptionId, SubscriptionStatus status, OperationType type) - { - SubscriptionId = subscriptionId; - Status = status; - Type = type; - } + public Guid SubscriptionId { get; private init; } = subscriptionId; + public SubscriptionStatus Status { get; private init; } = status; + public OperationType Type { get; private init; } = type; } public enum SubscriptionStatus diff --git a/src/LeanCode.Contracts/Validation/ValidationError.cs b/src/LeanCode.Contracts/Validation/ValidationError.cs index 36bfdbf..54fc6d3 100644 --- a/src/LeanCode.Contracts/Validation/ValidationError.cs +++ b/src/LeanCode.Contracts/Validation/ValidationError.cs @@ -1,15 +1,8 @@ namespace LeanCode.Contracts.Validation; -public class ValidationError +public class ValidationError(string propertyName, string errorMessage, int errorCode) { - public string PropertyName { get; } - public string ErrorMessage { get; } - public int ErrorCode { get; } - - public ValidationError(string propertyName, string errorMessage, int errorCode) - { - PropertyName = propertyName; - ErrorMessage = errorMessage; - ErrorCode = errorCode; - } + public string PropertyName { get; } = propertyName; + public string ErrorMessage { get; } = errorMessage; + public int ErrorCode { get; } = errorCode; } diff --git a/src/LeanCode.Contracts/Validation/ValidationResult.cs b/src/LeanCode.Contracts/Validation/ValidationResult.cs index 6de5cd7..4f01279 100644 --- a/src/LeanCode.Contracts/Validation/ValidationResult.cs +++ b/src/LeanCode.Contracts/Validation/ValidationResult.cs @@ -2,13 +2,8 @@ namespace LeanCode.Contracts.Validation; -public class ValidationResult +public class ValidationResult(IReadOnlyList? errors) { - public ImmutableList Errors { get; } + public ImmutableList Errors { get; } = errors is null ? [] : [.. errors]; public bool IsValid => Errors.Count == 0; - - public ValidationResult(IReadOnlyList? errors) - { - Errors = errors is null ? ImmutableList.Create() : errors.ToImmutableList(); - } } diff --git a/src/LeanCode.ContractsGenerator.Tests/ExampleBased/Glob.cs b/src/LeanCode.ContractsGenerator.Tests/ExampleBased/Glob.cs index 3de24b0..1bb6a4c 100644 --- a/src/LeanCode.ContractsGenerator.Tests/ExampleBased/Glob.cs +++ b/src/LeanCode.ContractsGenerator.Tests/ExampleBased/Glob.cs @@ -8,7 +8,7 @@ public class Glob [Fact] public void Globbing_finds_necessary_files() { - GlobCompiles(includes: new[] { "project/globs/**/*.cs" }, excludes: Array.Empty()) + GlobCompiles(includes: ["project/globs/**/*.cs"], excludes: []) .WithCommand("A.Command") .WithQuery("B.Query") .WithDto("A.Dto"); @@ -17,7 +17,7 @@ public void Globbing_finds_necessary_files() [Fact] public void Globbing_includes_correct_files() { - GlobCompiles(includes: new[] { "project/globs/A/*" }, excludes: Array.Empty()) + GlobCompiles(includes: ["project/globs/A/*"], excludes: []) .WithCommand("A.Command") .WithDto("A.Dto") .Without("B.Query"); @@ -26,7 +26,7 @@ public void Globbing_includes_correct_files() [Fact] public void Globbing_excludes_correct_files() { - GlobCompiles(includes: new[] { "project/globs/**" }, excludes: new[] { "**/Dto.cs" }) + GlobCompiles(includes: ["project/globs/**"], excludes: ["**/Dto.cs"]) .WithCommand("A.Command") .Without("A.Dto") .WithQuery("B.Query"); diff --git a/src/LeanCode.ContractsGenerator.Tests/Generation/ObjectExtensionsTests.cs b/src/LeanCode.ContractsGenerator.Tests/Generation/ObjectExtensionsTests.cs index 772f284..979709e 100644 --- a/src/LeanCode.ContractsGenerator.Tests/Generation/ObjectExtensionsTests.cs +++ b/src/LeanCode.ContractsGenerator.Tests/Generation/ObjectExtensionsTests.cs @@ -12,16 +12,15 @@ public void Null_is_converted() Assert.NotNull(vr.Null); } -#pragma warning disable SA1139 [Theory] [InlineData((byte)10)] [InlineData((sbyte)10)] - [InlineData((int)10)] - [InlineData((long)10)] [InlineData((short)10)] - [InlineData((uint)10)] - [InlineData((ulong)10)] [InlineData((ushort)10)] + [InlineData(10)] + [InlineData(10L)] + [InlineData(10U)] + [InlineData(10UL)] public void Number_is_converted(object value) { var vr = value.ToValueRef(); @@ -30,15 +29,14 @@ public void Number_is_converted(object value) } [Theory] - [InlineData((float)10.015000343322754)] - [InlineData((double)10.015000343322754)] + [InlineData(10.015000343322754f)] + [InlineData(10.015000343322754d)] public void FloatingPoint_is_converted(object value) { var vr = value.ToValueRef(); Assert.NotNull(vr.FloatingPoint); Assert.Equal(10.015000343322754, vr.FloatingPoint.Value); } -#pragma warning restore [Fact] public void String_is_converted() diff --git a/src/LeanCode.ContractsGenerator.Tests/Serialization/BinarySerializationTests.cs b/src/LeanCode.ContractsGenerator.Tests/Serialization/BinarySerializationTests.cs index 43e4ce6..f117933 100644 --- a/src/LeanCode.ContractsGenerator.Tests/Serialization/BinarySerializationTests.cs +++ b/src/LeanCode.ContractsGenerator.Tests/Serialization/BinarySerializationTests.cs @@ -8,7 +8,7 @@ public class BinarySerializationTests { private sealed record class BinaryDTO(Binary? Null, Binary? NullableValue, Binary Value); - private static readonly byte[] Hello = { 0x68, 0x65, 0x6c, 0x6c, 0x6f }; + private static readonly byte[] Hello = [.. "hello"u8]; private static readonly BinaryDTO DTO = new(null, Hello?.AsBinary(), Hello!.AsBinary()); diff --git a/src/LeanCode.ContractsGenerator.Tests/Serialization/CommandResultSerializationTests.cs b/src/LeanCode.ContractsGenerator.Tests/Serialization/CommandResultSerializationTests.cs index 30b88e2..47d2b03 100644 --- a/src/LeanCode.ContractsGenerator.Tests/Serialization/CommandResultSerializationTests.cs +++ b/src/LeanCode.ContractsGenerator.Tests/Serialization/CommandResultSerializationTests.cs @@ -9,7 +9,7 @@ namespace LeanCode.ContractsGenerator.Tests.Serialization; public class CommandResultSerializationTests { private static readonly CommandResult SampleCommandResult = CommandResult.NotValid( - new(new[] { new ValidationError("A property", "An error message", 1) }) + new([new ValidationError("A property", "An error message", 1)]) ); private const string Json = $$""" diff --git a/src/LeanCode.ContractsGenerator.Tests/Serialization/NotificationEnvelopeSerializationTests.cs b/src/LeanCode.ContractsGenerator.Tests/Serialization/NotificationEnvelopeSerializationTests.cs index 254b74b..5758764 100644 --- a/src/LeanCode.ContractsGenerator.Tests/Serialization/NotificationEnvelopeSerializationTests.cs +++ b/src/LeanCode.ContractsGenerator.Tests/Serialization/NotificationEnvelopeSerializationTests.cs @@ -2,7 +2,6 @@ using FluentAssertions; using FluentAssertions.Execution; using LeanCode.Contracts; -using LeanCode.Contracts.Validation; using Xunit; namespace LeanCode.ContractsGenerator.Tests.Serialization; @@ -11,10 +10,7 @@ public class NotificationEnvelopeSerializationTests { private const string NotificationId = "4d3b45e6-a2c1-4d6a-9e23-94e0d9f8ca01"; - private static readonly Topic SampleTopic = new() - { - EntityIds = new() { "Entity1", "Entity2" }, - }; + private static readonly Topic SampleTopic = new() { EntityIds = ["Entity1", "Entity2"] }; private static readonly Notification SampleNotification = new() { EntityId = "Entity1" }; diff --git a/src/LeanCode.ContractsGenerator.Tests/Serialization/SubscriptionEnvelopeSerializationTests.cs b/src/LeanCode.ContractsGenerator.Tests/Serialization/SubscriptionEnvelopeSerializationTests.cs index 99aec02..e1a9109 100644 --- a/src/LeanCode.ContractsGenerator.Tests/Serialization/SubscriptionEnvelopeSerializationTests.cs +++ b/src/LeanCode.ContractsGenerator.Tests/Serialization/SubscriptionEnvelopeSerializationTests.cs @@ -2,7 +2,6 @@ using FluentAssertions; using FluentAssertions.Execution; using LeanCode.Contracts; -using LeanCode.Contracts.Validation; using Xunit; namespace LeanCode.ContractsGenerator.Tests.Serialization; @@ -11,10 +10,7 @@ public class SubscriptionEnvelopeSerializationTests { private const string SubscriptionId = "4d3b45e6-a2c1-4d6a-9e23-94e0d9f8ca01"; - private static readonly Topic SampleTopic = new() - { - EntityIds = new() { "Entity1", "Entity2" }, - }; + private static readonly Topic SampleTopic = new() { EntityIds = ["Entity1", "Entity2"] }; private static readonly SubscriptionEnvelope SampleSubscriptionEnvelope = new() { diff --git a/src/LeanCode.ContractsGenerator/AnalyzeFailedException.cs b/src/LeanCode.ContractsGenerator/AnalyzeFailedException.cs index 6e11b55..b42e556 100644 --- a/src/LeanCode.ContractsGenerator/AnalyzeFailedException.cs +++ b/src/LeanCode.ContractsGenerator/AnalyzeFailedException.cs @@ -1,12 +1,7 @@ namespace LeanCode.ContractsGenerator; -public class AnalyzeFailedException : Exception +public class AnalyzeFailedException(IReadOnlyList errors) + : Exception(string.Join('\n', errors.Select(e => e.ToString()).Prepend("Analyze phase failed."))) { - public IReadOnlyList Errors { get; } - - public AnalyzeFailedException(IReadOnlyList errors) - : base(string.Join('\n', errors.Select(e => e.ToString()).Prepend("Analyze phase failed."))) - { - Errors = errors; - } + public IReadOnlyList Errors { get; } = errors; } diff --git a/src/LeanCode.ContractsGenerator/AnalyzerContext.cs b/src/LeanCode.ContractsGenerator/AnalyzerContext.cs index 53afbb7..60dee10 100644 --- a/src/LeanCode.ContractsGenerator/AnalyzerContext.cs +++ b/src/LeanCode.ContractsGenerator/AnalyzerContext.cs @@ -1,9 +1,7 @@ -using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace LeanCode.ContractsGenerator; -[SuppressMessage("?", "SA1313", Justification = "False positive.")] public readonly record struct AnalyzerContext(string Path) { public static readonly AnalyzerContext Empty = new(""); @@ -58,10 +56,7 @@ private AnalyzerContext Descend(string nextName) } } - private AnalyzerContext Append(string nextName) - { - return new($"{Path}{nextName}"); - } + private AnalyzerContext Append(string nextName) => new($"{Path}{nextName}"); private static string NameOf(TypeRef typeRef) { diff --git a/src/LeanCode.ContractsGenerator/Analyzers/AllAnalyzers.cs b/src/LeanCode.ContractsGenerator/Analyzers/AllAnalyzers.cs index 02c2b07..05e41bd 100644 --- a/src/LeanCode.ContractsGenerator/Analyzers/AllAnalyzers.cs +++ b/src/LeanCode.ContractsGenerator/Analyzers/AllAnalyzers.cs @@ -2,8 +2,8 @@ namespace LeanCode.ContractsGenerator.Analyzers; public class AllAnalyzers : IAnalyzer { - private readonly IReadOnlyList analyzers = new IAnalyzer[] - { + private readonly IReadOnlyList analyzers = + [ new InternalStructureCheck(), new KnownTypeCheck(), new ErrorCodesUniqueness(), @@ -12,10 +12,7 @@ public class AllAnalyzers : IAnalyzer new TopicWithoutNotificationCheck(), new TopicWithNullableNotificationCheck(), new TopicMustProduceInternalType(), - }; + ]; - public IEnumerable Analyze(Export export) - { - return analyzers.SelectMany(a => a.Analyze(export)); - } + public IEnumerable Analyze(Export export) => analyzers.SelectMany(a => a.Analyze(export)); } diff --git a/src/LeanCode.ContractsGenerator/AnalyzerCodes.cs b/src/LeanCode.ContractsGenerator/Analyzers/AnalyzerCodes.cs similarity index 100% rename from src/LeanCode.ContractsGenerator/AnalyzerCodes.cs rename to src/LeanCode.ContractsGenerator/Analyzers/AnalyzerCodes.cs diff --git a/src/LeanCode.ContractsGenerator/Analyzers/BaseAnalyzer.cs b/src/LeanCode.ContractsGenerator/Analyzers/BaseAnalyzer.cs index 23f4d8e..11045df 100644 --- a/src/LeanCode.ContractsGenerator/Analyzers/BaseAnalyzer.cs +++ b/src/LeanCode.ContractsGenerator/Analyzers/BaseAnalyzer.cs @@ -5,47 +5,33 @@ public class BaseAnalyzer : IAnalyzer public virtual IEnumerable Analyze(Export export) { var context = AnalyzerContext.Empty; - return export.Statements.SelectMany(s => AnalyzeStatement(context.Descend(s), s)).ToList(); + return [.. export.Statements.SelectMany(s => AnalyzeStatement(context.Descend(s), s))]; } - public virtual IEnumerable AnalyzeKnownType(AnalyzerContext context, KnownType knownType) - { - return Enumerable.Empty(); - } + public virtual IEnumerable AnalyzeKnownType(AnalyzerContext context, KnownType knownType) => []; - public virtual IEnumerable AnalyzeValueRef(AnalyzerContext context, ValueRef valueRef) - { - return Enumerable.Empty(); - } + public virtual IEnumerable AnalyzeValueRef(AnalyzerContext context, ValueRef valueRef) => []; public virtual IEnumerable AnalyzeGenericTypeRef( AnalyzerContext context, TypeRef typeRef, TypeRef.Types.Generic g - ) - { - return Enumerable.Empty(); - } + ) => []; public virtual IEnumerable AnalyzeInternalTypeRef( AnalyzerContext context, TypeRef typeRef, TypeRef.Types.Internal i - ) - { - return i.Arguments.SelectMany((a, i) => AnalyzeTypeRef(context.Argument(i, a), a)); - } + ) => i.Arguments.SelectMany((a, i) => AnalyzeTypeRef(context.Argument(i, a), a)); public virtual IEnumerable AnalyzeKnownTypeRef( AnalyzerContext context, TypeRef typeRef, TypeRef.Types.Known k - ) - { - return k + ) => + k .Arguments.SelectMany((a, i) => AnalyzeTypeRef(context.Argument(i, a), a)) .Concat(AnalyzeKnownType(context, k.Type)); - } public virtual IEnumerable AnalyzeTypeRef(AnalyzerContext context, TypeRef typeRef) { @@ -63,35 +49,26 @@ public virtual IEnumerable AnalyzeTypeRef(AnalyzerContext context, } else { - return Enumerable.Empty(); + return []; } } public virtual IEnumerable AnalyzeGenericParameter( AnalyzerContext context, GenericParameter genericParam - ) - { - return Enumerable.Empty(); - } + ) => []; public virtual IEnumerable AnalyzePositionalAttributeArgument( AnalyzerContext context, AttributeArgument arg, AttributeArgument.Types.Positional p - ) - { - return AnalyzeValueRef(context, p.Value); - } + ) => AnalyzeValueRef(context, p.Value); public virtual IEnumerable AnalyzeNamedAttributeArgument( AnalyzerContext context, AttributeArgument arg, AttributeArgument.Types.Named n - ) - { - return AnalyzeValueRef(context, n.Value); - } + ) => AnalyzeValueRef(context, n.Value); public virtual IEnumerable AnalyzeAttributeArgument(AnalyzerContext context, AttributeArgument arg) { @@ -105,48 +82,33 @@ public virtual IEnumerable AnalyzeAttributeArgument(AnalyzerContex } else { - return Enumerable.Empty(); + return []; } } - public virtual IEnumerable AnalyzeAttributeRef(AnalyzerContext context, AttributeRef attrRef) - { - return attrRef.Argument.SelectMany(a => AnalyzeAttributeArgument(context.Argument(a), a)); - } + public virtual IEnumerable AnalyzeAttributeRef(AnalyzerContext context, AttributeRef attrRef) => + attrRef.Argument.SelectMany(a => AnalyzeAttributeArgument(context.Argument(a), a)); - public virtual IEnumerable AnalyzePropertyRef(AnalyzerContext context, PropertyRef propRef) - { - return AnalyzeTypeRef(context, propRef.Type) + public virtual IEnumerable AnalyzePropertyRef(AnalyzerContext context, PropertyRef propRef) => + AnalyzeTypeRef(context, propRef.Type) .Concat(propRef.Attributes.SelectMany(a => AnalyzeAttributeRef(context.Attribute(a), a))); - } - public virtual IEnumerable AnalyzeConstantRef(AnalyzerContext context, ConstantRef constant) - { - return AnalyzeValueRef(context, constant.Value); - } + public virtual IEnumerable AnalyzeConstantRef(AnalyzerContext context, ConstantRef constant) => + AnalyzeValueRef(context, constant.Value); - public virtual IEnumerable AnalyzeEnumValue(AnalyzerContext context, EnumValue enumVal) - { - return Enumerable.Empty(); - } + public virtual IEnumerable AnalyzeEnumValue(AnalyzerContext context, EnumValue enumVal) => []; public virtual IEnumerable AnalyzeGroupErrorCode( AnalyzerContext context, ErrorCode errCode, ErrorCode.Types.Group g - ) - { - return AnalyzeErrorCodes(context, g.InnerCodes); - } + ) => AnalyzeErrorCodes(context, g.InnerCodes); public virtual IEnumerable AnalyzeSingleErrorCode( AnalyzerContext context, ErrorCode errCode, ErrorCode.Types.Single s - ) - { - return Enumerable.Empty(); - } + ) => []; public virtual IEnumerable AnalyzeErrorCode(AnalyzerContext context, ErrorCode errCode) { @@ -160,32 +122,29 @@ public virtual IEnumerable AnalyzeErrorCode(AnalyzerContext contex } else { - return Enumerable.Empty(); + return []; } } - public virtual IEnumerable AnalyzeErrorCodes(AnalyzerContext context, IEnumerable errCodes) - { - return errCodes.SelectMany(e => AnalyzeErrorCode(context.Descend(e), e)); - } + public virtual IEnumerable AnalyzeErrorCodes( + AnalyzerContext context, + IEnumerable errCodes + ) => errCodes.SelectMany(e => AnalyzeErrorCode(context.Descend(e), e)); - public virtual IEnumerable AnalyzeTypeDescriptor(AnalyzerContext context, TypeDescriptor descr) - { - return descr + public virtual IEnumerable AnalyzeTypeDescriptor(AnalyzerContext context, TypeDescriptor descr) => + descr .Extends.SelectMany(t => AnalyzeTypeRef(context.Extends(t), t)) .Concat( descr.GenericParameters.SelectMany((g, i) => AnalyzeGenericParameter(context.GenericParameter(i, g), g)) ) .Concat(descr.Properties.SelectMany(p => AnalyzePropertyRef(context.Descend(p), p))) .Concat(descr.Constants.SelectMany(c => AnalyzeConstantRef(context.Descend(c), c))); - } public virtual IEnumerable AnalyzeTypeDescriptorForQuery( AnalyzerContext context, TypeDescriptor descr - ) - { - return descr + ) => + descr .Extends.Where(e => e.Known is null || e.Known.Type != KnownType.Query) // Exclude `Query` type, as it will be checked by the `Return` check .SelectMany(t => AnalyzeTypeRef(context.Extends(t), t)) .Concat( @@ -193,14 +152,12 @@ TypeDescriptor descr ) .Concat(descr.Properties.SelectMany(p => AnalyzePropertyRef(context.Descend(p), p))) .Concat(descr.Constants.SelectMany(c => AnalyzeConstantRef(context.Descend(c), c))); - } public virtual IEnumerable AnalyzeTypeDescriptorForOperation( AnalyzerContext context, TypeDescriptor descr - ) - { - return descr + ) => + descr .Extends.Where(e => e.Known is null || e.Known.Type != KnownType.Operation) // Exclude `Operation` type, as it will be checked by the `Return` check .SelectMany(t => AnalyzeTypeRef(context.Extends(t), t)) .Concat( @@ -208,77 +165,60 @@ TypeDescriptor descr ) .Concat(descr.Properties.SelectMany(p => AnalyzePropertyRef(context.Descend(p), p))) .Concat(descr.Constants.SelectMany(c => AnalyzeConstantRef(context.Descend(c), c))); - } public virtual IEnumerable AnalyzeTypeDescriptorForTopic( AnalyzerContext context, TypeDescriptor descr - ) - { - return descr + ) => + descr .Extends.Where(e => e.Known is null || e.Known.Type != KnownType.Topic) .SelectMany(t => AnalyzeTypeRef(context.Extends(t), t)) .Concat(descr.Properties.SelectMany(p => AnalyzePropertyRef(context.Descend(p), p))) .Concat(descr.Constants.SelectMany(c => AnalyzeConstantRef(context.Descend(c), c))); - } public virtual IEnumerable AnalyzeDTO( AnalyzerContext context, Statement stmt, Statement.Types.DTO dto - ) - { - return AnalyzeTypeDescriptor(context, dto.TypeDescriptor); - } + ) => AnalyzeTypeDescriptor(context, dto.TypeDescriptor); public virtual IEnumerable AnalyzeEnum( AnalyzerContext context, Statement stmt, Statement.Types.Enum @enum - ) - { - return @enum.Members.SelectMany(m => AnalyzeEnumValue(context.Descend(m), m)); - } + ) => @enum.Members.SelectMany(m => AnalyzeEnumValue(context.Descend(m), m)); public virtual IEnumerable AnalyzeQuery( AnalyzerContext context, Statement stmt, Statement.Types.Query query - ) - { - return AnalyzeTypeDescriptorForQuery(context, query.TypeDescriptor) + ) => + AnalyzeTypeDescriptorForQuery(context, query.TypeDescriptor) .Concat(AnalyzeTypeRef(context.Returns(query.ReturnType), query.ReturnType)); - } public virtual IEnumerable AnalyzeCommand( AnalyzerContext context, Statement stmt, Statement.Types.Command command - ) - { - return AnalyzeTypeDescriptor(context, command.TypeDescriptor) + ) => + AnalyzeTypeDescriptor(context, command.TypeDescriptor) .Concat(AnalyzeErrorCodes(context.ErrorCodes(), command.ErrorCodes)); - } public virtual IEnumerable AnalyzeOperation( AnalyzerContext context, Statement stmt, Statement.Types.Operation operation - ) - { - return AnalyzeTypeDescriptorForOperation(context, operation.TypeDescriptor) + ) => + AnalyzeTypeDescriptorForOperation(context, operation.TypeDescriptor) .Concat(AnalyzeTypeRef(context.Returns(operation.ReturnType), operation.ReturnType)); - } public virtual IEnumerable AnalyzeTopic( AnalyzerContext context, Statement stmt, Statement.Types.Topic topic - ) - { - return AnalyzeTypeDescriptorForTopic(context, topic.TypeDescriptor) + ) => + AnalyzeTypeDescriptorForTopic(context, topic.TypeDescriptor) .Concat(topic.Notifications.SelectMany(n => AnalyzeTypeRef(context.Returns(n.Type), n.Type))); - } public virtual IEnumerable AnalyzeStatement(AnalyzerContext context, Statement stmt) { @@ -313,7 +253,7 @@ IEnumerable AnalyzeInner(AnalyzerContext context, Statement stmt) } else { - return Enumerable.Empty(); + return []; } } } diff --git a/src/LeanCode.ContractsGenerator/Analyzers/ErrorCodesUniqueness.cs b/src/LeanCode.ContractsGenerator/Analyzers/ErrorCodesUniqueness.cs index 3980ec2..d69f7e4 100644 --- a/src/LeanCode.ContractsGenerator/Analyzers/ErrorCodesUniqueness.cs +++ b/src/LeanCode.ContractsGenerator/Analyzers/ErrorCodesUniqueness.cs @@ -19,14 +19,7 @@ IEnumerable errCodes static IEnumerable Flatten(ErrorCode errCode) { - if (errCode.Single is ErrorCode.Types.Single s) - { - return new[] { s }; - } - else - { - return errCode.Group.InnerCodes.SelectMany(Flatten); - } + return errCode.Single is ErrorCode.Types.Single s ? [s] : errCode.Group.InnerCodes.SelectMany(Flatten); } } @@ -34,27 +27,15 @@ public override IEnumerable Analyze(Export export) { var context = AnalyzerContext.Empty; - return export.Statements.SelectMany(s => AnalyzeStatement(context.Descend(s), s)).ToList(); + return [.. export.Statements.SelectMany(s => AnalyzeStatement(context.Descend(s), s))]; } public override IEnumerable AnalyzeCommand( AnalyzerContext context, Statement stmt, Statement.Types.Command command - ) - { - return AnalyzeErrorCodes(context.ErrorCodes(), command.ErrorCodes); - } + ) => AnalyzeErrorCodes(context.ErrorCodes(), command.ErrorCodes); - public override IEnumerable AnalyzeStatement(AnalyzerContext context, Statement stmt) - { - if (stmt.Command is Statement.Types.Command cmd) - { - return AnalyzeCommand(context, stmt, cmd); - } - else - { - return Enumerable.Empty(); - } - } + public override IEnumerable AnalyzeStatement(AnalyzerContext context, Statement stmt) => + stmt.Command is Statement.Types.Command cmd ? AnalyzeCommand(context, stmt, cmd) : []; } diff --git a/src/LeanCode.ContractsGenerator/Analyzers/ExternalTypeCheck.cs b/src/LeanCode.ContractsGenerator/Analyzers/ExternalTypeCheck.cs index f7b12c8..64ced18 100644 --- a/src/LeanCode.ContractsGenerator/Analyzers/ExternalTypeCheck.cs +++ b/src/LeanCode.ContractsGenerator/Analyzers/ExternalTypeCheck.cs @@ -4,7 +4,7 @@ namespace LeanCode.ContractsGenerator.Analyzers; public class ExternalTypeCheck : BaseAnalyzer { - private ImmutableHashSet knownTypes = ImmutableHashSet.Empty; + private ImmutableHashSet knownTypes = []; public override IEnumerable Analyze(Export export) { @@ -24,19 +24,16 @@ TypeRef.Types.Internal i } else { - return new[] - { + return + [ new AnalyzeError( AnalyzerCodes.InternalTypeIsNotKnown, $"Internal type `{i.Name}` is not known.", context ), - }; + ]; } } - private static ImmutableHashSet GatherTypes(Export export) - { - return export.Statements.Select(s => s.Name).ToImmutableHashSet(); - } + private static ImmutableHashSet GatherTypes(Export export) => [.. export.Statements.Select(s => s.Name)]; } diff --git a/src/LeanCode.ContractsGenerator/Analyzers/InvalidTypeCheck.cs b/src/LeanCode.ContractsGenerator/Analyzers/InvalidTypeCheck.cs index 1596d5c..a3088ee 100644 --- a/src/LeanCode.ContractsGenerator/Analyzers/InvalidTypeCheck.cs +++ b/src/LeanCode.ContractsGenerator/Analyzers/InvalidTypeCheck.cs @@ -15,10 +15,7 @@ TypeRef.Types.Internal i { if (InvalidTypes.TryGetValue(i.Name, out var msg)) { - return new[] - { - new AnalyzeError(AnalyzerCodes.UnsupportedType, $"Type `{i.Name}` is unsupported. {msg}", context), - }; + return [new AnalyzeError(AnalyzerCodes.UnsupportedType, $"Type `{i.Name}` is unsupported. {msg}", context)]; } else { diff --git a/src/LeanCode.ContractsGenerator/Analyzers/KnownTypeCheck.cs b/src/LeanCode.ContractsGenerator/Analyzers/KnownTypeCheck.cs index 2222af3..c2ffc9c 100644 --- a/src/LeanCode.ContractsGenerator/Analyzers/KnownTypeCheck.cs +++ b/src/LeanCode.ContractsGenerator/Analyzers/KnownTypeCheck.cs @@ -2,11 +2,9 @@ namespace LeanCode.ContractsGenerator.Analyzers; public class KnownTypeCheck : BaseAnalyzer { - private static readonly IReadOnlySet ValidKnownTypeValues = Enum.GetValues().ToHashSet(); - public override IEnumerable AnalyzeKnownType(AnalyzerContext context, KnownType knownType) { - if (!ValidKnownTypeValues.Contains(knownType)) + if (!Enum.IsDefined(knownType)) { yield return new( AnalyzerCodes.UnsupportedKnownType, diff --git a/src/LeanCode.ContractsGenerator/Compilation/CompilationFailedException.cs b/src/LeanCode.ContractsGenerator/Compilation/CompilationFailedException.cs index 4bf7c6e..8bedd17 100644 --- a/src/LeanCode.ContractsGenerator/Compilation/CompilationFailedException.cs +++ b/src/LeanCode.ContractsGenerator/Compilation/CompilationFailedException.cs @@ -17,12 +17,13 @@ public CompilationFailedException(ImmutableArray diagnostics) public CompilationFailedException(string message) : base(message) { - Diagnostics = ImmutableArray.Empty; + Diagnostics = []; } private static string GetDiagnosticsMessage(ImmutableArray diagnostics) { var sb = new StringBuilder(); + foreach (var d in diagnostics) { sb.AppendLine($"[{d.Severity}] {d.GetMessage()} at {FormatLocation(d.Location)}"); diff --git a/src/LeanCode.ContractsGenerator/Compilation/CompiledContracts.cs b/src/LeanCode.ContractsGenerator/Compilation/CompiledContracts.cs index 7b044e9..2ad407d 100644 --- a/src/LeanCode.ContractsGenerator/Compilation/CompiledContracts.cs +++ b/src/LeanCode.ContractsGenerator/Compilation/CompiledContracts.cs @@ -4,24 +4,13 @@ namespace LeanCode.ContractsGenerator.Compilation; -public sealed class CompiledContracts +public sealed class CompiledContracts(IReadOnlyCollection compilations, string projectName) { - private readonly IReadOnlyCollection compilations; + public ContractTypes Types { get; } = new(compilations); + public string ProjectName { get; } = projectName; - public ContractTypes Types { get; } - public string ProjectName { get; } - - public CompiledContracts(IReadOnlyCollection compilations, string projectName) - { - this.compilations = compilations; - ProjectName = projectName; - - Types = new(compilations); - } - - public IEnumerable ListAllTypes() - { - return compilations.SelectMany(c => + public IEnumerable ListAllTypes() => + compilations.SelectMany(c => c.SyntaxTrees.SelectMany(t => { var model = c.GetSemanticModel(t); @@ -33,5 +22,4 @@ public IEnumerable ListAllTypes() .OfType(); }) ); - } } diff --git a/src/LeanCode.ContractsGenerator/Compilation/ContractTypes.cs b/src/LeanCode.ContractsGenerator/Compilation/ContractTypes.cs index 8996fcf..da88829 100644 --- a/src/LeanCode.ContractsGenerator/Compilation/ContractTypes.cs +++ b/src/LeanCode.ContractsGenerator/Compilation/ContractTypes.cs @@ -30,7 +30,9 @@ public sealed class ContractTypes private HashSet Equatable { get; } +#pragma warning disable IDE0290 // Use primary constructor public ContractTypes(IReadOnlyCollection compilations) +#pragma warning restore IDE0290 // Use primary constructor { QueryType = GetUnboundTypeSymbols(compilations, typeof(IQuery<>)); CommandType = GetTypeSymbols(compilations); @@ -54,16 +56,14 @@ private static HashSet GetTypeSymbols(IReadOnlyCollection(SymbolEqualityComparer.Default); + foreach (var c in compilations) { - var type = c.GetTypeByMetadataName(name); - if (type is null) - { - throw new CompilationFailedException( + var type = + c.GetTypeByMetadataName(name) + ?? throw new CompilationFailedException( $"Cannot locate type {name} in compilation unit `{c.AssemblyName ?? "UNKNOWN"}`." ); - } - result.Add(type); } @@ -77,53 +77,43 @@ Type type { var name = type.FullName!; var result = new HashSet(SymbolEqualityComparer.Default); + foreach (var c in compilations) { - var t = c.GetTypeByMetadataName(name)?.ConstructUnboundGenericType(); - if (t is null) - { - throw new CompilationFailedException( + var t = + (c.GetTypeByMetadataName(name)?.ConstructUnboundGenericType()) + ?? throw new CompilationFailedException( $"Cannot locate generic type {name} in compilation unit `{c.AssemblyName ?? "UNKNOWN"}`." ); - } - result.Add(t); } return result; } - public bool IsQuery(ITypeSymbol symbol) - { - return symbol is INamedTypeSymbol ns - && !ns.IsUnboundGenericType - && !ns.IsAbstract - && ns.AllInterfaces.Any(IsQueryType); - } - - public bool IsCommand(ITypeSymbol symbol) - { - return symbol is INamedTypeSymbol ns - && !ns.IsUnboundGenericType - && !ns.IsAbstract - && ns.AllInterfaces.Any(IsCommandType); - } - - public bool IsOperation(ITypeSymbol symbol) - { - return symbol is INamedTypeSymbol ns - && !ns.IsUnboundGenericType - && !ns.IsAbstract - && ns.AllInterfaces.Any(IsOperationType); - } - - public bool IsTopic(ITypeSymbol symbol) - { - return symbol is INamedTypeSymbol ns - && !ns.IsUnboundGenericType - && !ns.IsAbstract - && ns.AllInterfaces.Any(IsTopicType); - } + public bool IsQuery(ITypeSymbol symbol) => + symbol is INamedTypeSymbol ns + && !ns.IsUnboundGenericType + && !ns.IsAbstract + && ns.AllInterfaces.Any(IsQueryType); + + public bool IsCommand(ITypeSymbol symbol) => + symbol is INamedTypeSymbol ns + && !ns.IsUnboundGenericType + && !ns.IsAbstract + && ns.AllInterfaces.Any(IsCommandType); + + public bool IsOperation(ITypeSymbol symbol) => + symbol is INamedTypeSymbol ns + && !ns.IsUnboundGenericType + && !ns.IsAbstract + && ns.AllInterfaces.Any(IsOperationType); + + public bool IsTopic(ITypeSymbol symbol) => + symbol is INamedTypeSymbol ns + && !ns.IsUnboundGenericType + && !ns.IsAbstract + && ns.AllInterfaces.Any(IsTopicType); public ITypeSymbol ExtractQueryResult(ITypeSymbol symbol) { @@ -201,27 +191,21 @@ public bool IsExcludeFromContractsGenerationType(ITypeSymbol? i) => public bool IsAttributeUsageType(ITypeSymbol i) => AttributeUsageAttribute.Contains(i); - public bool IsReadOnlyDictionary(ITypeSymbol i) - { - return i is INamedTypeSymbol ns - && ns.IsGenericType - && ( - ReadOnlyDictionary.Contains(ns.ConstructUnboundGenericType()) - || Dictionary.Contains(ns.ConstructUnboundGenericType()) - ); - } + public bool IsReadOnlyDictionary(ITypeSymbol i) => + i is INamedTypeSymbol ns + && ns.IsGenericType + && ( + ReadOnlyDictionary.Contains(ns.ConstructUnboundGenericType()) + || Dictionary.Contains(ns.ConstructUnboundGenericType()) + ); - public bool IsRecordEquatable(ITypeSymbol i) - { - return i is INamedTypeSymbol ns - && ns.IsGenericType - && ns.TypeArguments is [INamedTypeSymbol namedType] - && namedType.IsRecord - && Equatable.Contains(ns.ConstructUnboundGenericType()); - } + public bool IsRecordEquatable(ITypeSymbol i) => + i is INamedTypeSymbol ns + && ns.IsGenericType + && ns.TypeArguments is [INamedTypeSymbol namedType] + && namedType.IsRecord + && Equatable.Contains(ns.ConstructUnboundGenericType()); - public static bool IsRecordEqualityContract(IPropertySymbol i) - { - return i.ContainingType is INamedTypeSymbol ns && ns.IsRecord && i.Name == RecordEqualityContractPropertyName; - } + public static bool IsRecordEqualityContract(IPropertySymbol i) => + i.ContainingType is INamedTypeSymbol ns && ns.IsRecord && i.Name == RecordEqualityContractPropertyName; } diff --git a/src/LeanCode.ContractsGenerator/Compilation/ContractsCompiler.cs b/src/LeanCode.ContractsGenerator/Compilation/ContractsCompiler.cs index 80f66e6..b8df78b 100644 --- a/src/LeanCode.ContractsGenerator/Compilation/ContractsCompiler.cs +++ b/src/LeanCode.ContractsGenerator/Compilation/ContractsCompiler.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Collections.Immutable; using System.Reflection; using Microsoft.CodeAnalysis; @@ -11,37 +12,23 @@ namespace LeanCode.ContractsGenerator.Compilation; public static class ContractsCompiler { - public static readonly ImmutableHashSet ReferenceAssemblyNames = ImmutableHashSet.Empty; - public static readonly ImmutableHashSet DefaultAssemblyNames = ImmutableHashSet.CreateRange( - new string[] - { - "System.Collections", - "System.Linq", - "System.Net.Http", - "System.Runtime", - "System.Runtime.Extensions", - } + public static readonly FrozenSet DefaultAssemblyNames = FrozenSet.Create( + StringComparer.InvariantCultureIgnoreCase, + "System.Collections", + "System.Linq", + "System.Net.Http", + "System.Runtime", + "System.Runtime.Extensions" ); - public static readonly ImmutableHashSet LeanCodeAssemblyNames = ImmutableHashSet.CreateRange( - new[] { "LeanCode.Contracts" } + public static readonly FrozenSet LeanCodeAssemblyNames = FrozenSet.Create( + StringComparer.InvariantCultureIgnoreCase, + "LeanCode.Contracts" ); - private static bool IsWantedReferenceAssembly(CompilationLibrary cl) - { - return cl.Type == "referenceassembly" - && ReferenceAssemblyNames.Contains(cl.Name, StringComparer.InvariantCultureIgnoreCase); - } - - private static bool IsWantedLeanCodeAssembly(CompilationLibrary cl) - { - return LeanCodeAssemblyNames.Contains(cl.Name, StringComparer.InvariantCultureIgnoreCase); - } + private static bool IsWantedDefaultAssembly(CompilationLibrary cl) => DefaultAssemblyNames.Contains(cl.Name); - private static bool IsWantedDefaultAssembly(CompilationLibrary cl) - { - return DefaultAssemblyNames.Contains(cl.Name, StringComparer.InvariantCultureIgnoreCase); - } + private static bool IsWantedLeanCodeAssembly(CompilationLibrary cl) => LeanCodeAssemblyNames.Contains(cl.Name); private static readonly Assembly ExecutingAssembly = Assembly.GetExecutingAssembly(); @@ -49,21 +36,18 @@ private static bool IsWantedDefaultAssembly(CompilationLibrary cl) Path.GetDirectoryName(ExecutingAssembly.Location)! ); - public static readonly ImmutableList DefaultAssemblies = DependencyContext - .Load(ExecutingAssembly)! - .CompileLibraries.Where(cl => - IsWantedReferenceAssembly(cl) || IsWantedLeanCodeAssembly(cl) || IsWantedDefaultAssembly(cl) - ) - .SelectMany(cl => cl.ResolveReferencePaths(Resolver)) - .Select(path => MetadataReference.CreateFromFile(path)) - .ToImmutableList(); + public static readonly ImmutableList DefaultAssemblies = + [ + .. DependencyContext + .Load(ExecutingAssembly)! + .CompileLibraries.Where(cl => IsWantedDefaultAssembly(cl) || IsWantedLeanCodeAssembly(cl)) + .SelectMany(cl => cl.ResolveReferencePaths(Resolver)) + .Select(path => MetadataReference.CreateFromFile(path)), + ]; public static Task<(CompiledContracts Compiled, List External)> CompileProjectsAsync( IEnumerable projectPaths - ) - { - return CompileProjectsAsync(projectPaths, ImmutableDictionary.Empty); - } + ) => CompileProjectsAsync(projectPaths, ImmutableDictionary.Empty); public static async Task<(CompiledContracts Compiled, List External)> CompileProjectsAsync( IEnumerable projectPaths, @@ -121,7 +105,7 @@ public static async Task CompileGlobAsync(Matcher matcher, Di public static CompiledContracts CompileCode(string contractText, string name) { var contractTree = CSharpSyntaxTree.ParseText(contractText); - return CompileTrees(new() { contractTree }, name); + return CompileTrees([contractTree], name); } private static CompiledContracts CompileTrees(List trees, string name) @@ -135,14 +119,12 @@ private static CompiledContracts CompileTrees(List trees, string nam return Compile(compilation, name); } - private static CSharpCompilationOptions PrepareCompilationOptions() - { - return new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) + private static CSharpCompilationOptions PrepareCompilationOptions() => + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) .WithConcurrentBuild(true) .WithAllowUnsafe(false) .WithNullableContextOptions(NullableContextOptions.Annotations) .WithPlatform(Platform.AnyCpu); - } [System.Diagnostics.CodeAnalysis.SuppressMessage( "?", @@ -186,10 +168,8 @@ private static List TryLoadEmbeddedContracts(IReadOnlyCollection { compilation }, name); - } + private static CompiledContracts Compile(CSharpCompilation compilation, string name) => + Compile([compilation], name); private static CompiledContracts Compile(IReadOnlyCollection compilations, string name) { diff --git a/src/LeanCode.ContractsGenerator/Compilation/InvalidProjectException.cs b/src/LeanCode.ContractsGenerator/Compilation/InvalidProjectException.cs index 801c345..4692fde 100644 --- a/src/LeanCode.ContractsGenerator/Compilation/InvalidProjectException.cs +++ b/src/LeanCode.ContractsGenerator/Compilation/InvalidProjectException.cs @@ -1,7 +1,3 @@ namespace LeanCode.ContractsGenerator.Compilation; -public class InvalidProjectException : Exception -{ - public InvalidProjectException(string msg) - : base(msg) { } -} +public class InvalidProjectException(string msg) : Exception(msg); diff --git a/src/LeanCode.ContractsGenerator/Compilation/MSBuild/LooseAssemblyVersionLoader.cs b/src/LeanCode.ContractsGenerator/Compilation/MSBuild/LooseAssemblyVersionLoader.cs index 20e6122..58da352 100644 --- a/src/LeanCode.ContractsGenerator/Compilation/MSBuild/LooseAssemblyVersionLoader.cs +++ b/src/LeanCode.ContractsGenerator/Compilation/MSBuild/LooseAssemblyVersionLoader.cs @@ -11,16 +11,19 @@ namespace LeanCode.ContractsGenerator.Compilation.MSBuild; internal static class LooseVersionAssemblyLoader { private static readonly Dictionary PathsToAssemblies = new(StringComparer.OrdinalIgnoreCase); - private static readonly Dictionary NamesToAssemblies = new(); + private static readonly Dictionary NamesToAssemblies = []; +#if NET9_0_OR_GREATER + private static readonly Lock Guard = new(); +#else private static readonly object Guard = new(); - private static readonly string[] Extensions = new[] { "ni.dll", "ni.exe", "dll", "exe" }; +#endif + private static readonly IReadOnlyCollection Extensions = ["ni.dll", "ni.exe", "dll", "exe"]; /// /// Register an assembly loader that will load assemblies with higher version than what was requested. /// - public static void Register(string searchPath) - { + public static void Register(string searchPath) => AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext context, AssemblyName assemblyName) => { lock (Guard) @@ -33,7 +36,6 @@ public static void Register(string searchPath) return TryResolveAssemblyFromPaths_NoLock(context, assemblyName, searchPath); } }; - } private static Assembly TryResolveAssemblyFromPaths_NoLock( AssemblyLoadContext context, @@ -41,16 +43,16 @@ private static Assembly TryResolveAssemblyFromPaths_NoLock( string searchPath ) { - foreach ( - var cultureSubfolder in string.IsNullOrEmpty(assemblyName.CultureName) - // If no culture is specified, attempt to load directly from - // the known dependency paths. - ? new[] { string.Empty } - // Search for satellite assemblies in culture subdirectories - // of the assembly search directories, but fall back to the - // bare search directory if that fails. - : new[] { assemblyName.CultureName, string.Empty } - ) + Span cultureSubfolders = string.IsNullOrEmpty(assemblyName.CultureName) + // If no culture is specified, attempt to load directly from + // the known dependency paths. + ? [string.Empty] + // Search for satellite assemblies in culture subdirectories + // of the assembly search directories, but fall back to the + // bare search directory if that fails. + : [assemblyName.CultureName, string.Empty]; + + foreach (var cultureSubfolder in cultureSubfolders) { foreach (var extension in Extensions) { diff --git a/src/LeanCode.ContractsGenerator/Compilation/MSBuild/MSBuildHelper.cs b/src/LeanCode.ContractsGenerator/Compilation/MSBuild/MSBuildHelper.cs index d0ce259..ef6f0a4 100644 --- a/src/LeanCode.ContractsGenerator/Compilation/MSBuild/MSBuildHelper.cs +++ b/src/LeanCode.ContractsGenerator/Compilation/MSBuild/MSBuildHelper.cs @@ -14,7 +14,7 @@ namespace LeanCode.ContractsGenerator.Compilation.MSBuild; public static class MSBuildHelper { - private static readonly string[] RestoreTarget = new string[] { "Restore" }; + private static readonly string[] RestoreTarget = ["Restore"]; private static readonly ImmutableDictionary GlobalProperties = ImmutableDictionary.CreateRange( new Dictionary diff --git a/src/LeanCode.ContractsGenerator/Compilation/ProjectLoader.cs b/src/LeanCode.ContractsGenerator/Compilation/ProjectLoader.cs index d8024ba..1231f6c 100644 --- a/src/LeanCode.ContractsGenerator/Compilation/ProjectLoader.cs +++ b/src/LeanCode.ContractsGenerator/Compilation/ProjectLoader.cs @@ -6,15 +6,10 @@ namespace LeanCode.ContractsGenerator.Compilation; -public sealed class ProjectLoader : IDisposable +public sealed class ProjectLoader(ImmutableDictionary properties) : IDisposable { - private readonly List projects = new(); - private readonly MSBuildWorkspace msbuildWorkspace; - - public ProjectLoader(ImmutableDictionary properties) - { - msbuildWorkspace = MSBuildHelper.CreateWorkspace(properties); - } + private readonly MSBuildWorkspace msbuildWorkspace = MSBuildHelper.CreateWorkspace(properties); + private readonly List projects = []; public async Task LoadProjectsAsync(IEnumerable projectPaths) { diff --git a/src/LeanCode.ContractsGenerator/Generation/ContractsGenerator.cs b/src/LeanCode.ContractsGenerator/Generation/ContractsGenerator.cs index e6fffe3..92563d7 100644 --- a/src/LeanCode.ContractsGenerator/Generation/ContractsGenerator.cs +++ b/src/LeanCode.ContractsGenerator/Generation/ContractsGenerator.cs @@ -5,23 +5,15 @@ namespace LeanCode.ContractsGenerator.Generation; -public class ContractsGenerator +public class ContractsGenerator(CompiledContracts contracts) { - private readonly CompiledContracts contracts; - - private readonly TypeRefFactory typeRef; - - public ContractsGenerator(CompiledContracts contracts) - { - this.contracts = contracts; - - typeRef = new(contracts); - } + private readonly TypeRefFactory typeRef = new(contracts); public Export Generate() { var export = GenerateCore(); - return Analyze(export); + Analyze(export); + return export; } public Export Generate(List externalContracts, bool excludeExternalContractsFromOutput) @@ -56,17 +48,14 @@ private Export GenerateCore() return export; } - private static Export Analyze(Export export) + private static void Analyze(Export export) { var errors = new Analyzers.AllAnalyzers().Analyze(export).ToList(); + if (errors.Count > 0) { throw new AnalyzeFailedException(errors); } - else - { - return export; - } } private Statement? ProcessType(INamedTypeSymbol? symbol) @@ -159,57 +148,38 @@ void MapProperties(INamedTypeSymbol symbol, TypeDescriptor descriptor) } } - private bool IsNotIgnored([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] INamedTypeSymbol? symbol) - { - return !IsIgnored(symbol); - } + private bool IsNotIgnored([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] INamedTypeSymbol? symbol) => + !IsIgnored(symbol); - private bool IsIgnored([System.Diagnostics.CodeAnalysis.NotNullWhen(false)] INamedTypeSymbol? symbol) - { - return symbol is null - || symbol.SpecialType == SpecialType.System_Object - || symbol.SpecialType == SpecialType.System_ValueType - || symbol.SpecialType == SpecialType.System_Enum - || ErrorCodes.IsErrorCode(symbol) - || contracts.Types.IsProduceNotificationType(symbol) - || contracts.Types.IsAttributeUsageType(symbol) - || contracts.Types.IsRecordEquatable(symbol); - } + private bool IsIgnored([System.Diagnostics.CodeAnalysis.NotNullWhen(false)] INamedTypeSymbol? symbol) => + symbol is null + || symbol.SpecialType == SpecialType.System_Object + || symbol.SpecialType == SpecialType.System_ValueType + || symbol.SpecialType == SpecialType.System_Enum + || ErrorCodes.IsErrorCode(symbol) + || contracts.Types.IsProduceNotificationType(symbol) + || contracts.Types.IsAttributeUsageType(symbol) + || contracts.Types.IsRecordEquatable(symbol); - private bool IsExcluded(ISymbol symbol) - { - return (symbol is IPropertySymbol ps && ContractTypes.IsRecordEqualityContract(ps)) - || symbol.GetAttributes().Any(a => contracts.Types.IsExcludeFromContractsGenerationType(a.AttributeClass)); - } + private bool IsExcluded(ISymbol symbol) => + (symbol is IPropertySymbol ps && ContractTypes.IsRecordEqualityContract(ps)) + || symbol.GetAttributes().Any(a => contracts.Types.IsExcludeFromContractsGenerationType(a.AttributeClass)); - private static HashSet GatherBaseProperties(INamedTypeSymbol ns) - { - return ns - .AllInterfaces.SelectMany(i => i.GetMembers()) - .OfType() - .Select(p => p.Name) - .ToHashSet(); - } + private static HashSet GatherBaseProperties(INamedTypeSymbol ns) => + [.. ns.AllInterfaces.SelectMany(i => i.GetMembers()).OfType().Select(p => p.Name)]; - private static bool AlreadyImplemented(IPropertySymbol prop, HashSet baseProps) - { - return baseProps.Contains(prop.Name); - } + private static bool AlreadyImplemented(IPropertySymbol prop, HashSet baseProps) => + baseProps.Contains(prop.Name); - private GenericParameter ToParam(ITypeParameterSymbol ts) - { - return new() { Name = ts.Name }; - } + private GenericParameter ToParam(ITypeParameterSymbol ts) => new() { Name = ts.Name }; - private ConstantRef ToConstant(IFieldSymbol fs) - { - return new() + private ConstantRef ToConstant(IFieldSymbol fs) => + new() { Name = fs.Name, Value = fs.ConstantValue.ToValueRef(), Comment = fs.GetComments(), }; - } private PropertyRef ToProperty(IPropertySymbol ps) { @@ -282,7 +252,7 @@ static AttributeArgument ToPositionalArgument(object? v, int i) static IEnumerable FlattenPositionalArray(TypedConstant a) { - return a.Kind == TypedConstantKind.Array ? a.Values.Select(v => v.Value) : new[] { a.Value }; + return a.Kind == TypedConstantKind.Array ? a.Values.Select(v => v.Value) : [a.Value]; } static IEnumerable<(string Key, object? Value)> FlattenNamedArray(KeyValuePair a) @@ -293,7 +263,7 @@ static AttributeArgument ToPositionalArgument(object? v, int i) } else { - return new[] { (a.Key, a.Value.Value) }; + return [(a.Key, a.Value.Value)]; } } } diff --git a/src/LeanCode.ContractsGenerator/Generation/ErrorCodes.cs b/src/LeanCode.ContractsGenerator/Generation/ErrorCodes.cs index cfd1416..63272ba 100644 --- a/src/LeanCode.ContractsGenerator/Generation/ErrorCodes.cs +++ b/src/LeanCode.ContractsGenerator/Generation/ErrorCodes.cs @@ -7,25 +7,20 @@ public static class ErrorCodes { private const string ErrorCodesName = "ErrorCodes"; - public static bool IsErrorCode(ISymbol? sym) - { - return sym?.Name == ErrorCodesName || (sym?.ContainingSymbol is not null && IsErrorCode(sym.ContainingSymbol)); - } + public static bool IsErrorCode(ISymbol? sym) => + sym?.Name == ErrorCodesName || (sym?.ContainingSymbol is not null && IsErrorCode(sym.ContainingSymbol)); public static IEnumerable Extract(INamedTypeSymbol symbol) { - var errCodes = symbol - .GetMembers() - .OfType() - .Where(s => s.Name == ErrorCodesName) - .SingleOrDefault(); + var errCodes = symbol.GetMembers().OfType().SingleOrDefault(s => s.Name == ErrorCodesName); + if (errCodes is not null) { return MapCodes(errCodes); } else { - return Enumerable.Empty(); + return []; } static IEnumerable MapCodes(INamedTypeSymbol errCodes) diff --git a/src/LeanCode.ContractsGenerator/Generation/GenerationFailedException.cs b/src/LeanCode.ContractsGenerator/Generation/GenerationFailedException.cs index 579f0c1..e46552c 100644 --- a/src/LeanCode.ContractsGenerator/Generation/GenerationFailedException.cs +++ b/src/LeanCode.ContractsGenerator/Generation/GenerationFailedException.cs @@ -1,7 +1,3 @@ namespace LeanCode.ContractsGenerator.Generation; -public class GenerationFailedException : Exception -{ - public GenerationFailedException(string message) - : base(message) { } -} +public class GenerationFailedException(string message) : Exception(message); diff --git a/src/LeanCode.ContractsGenerator/Generation/IEnumerableExtensions.cs b/src/LeanCode.ContractsGenerator/Generation/IEnumerableExtensions.cs index aa8547a..e786c73 100644 --- a/src/LeanCode.ContractsGenerator/Generation/IEnumerableExtensions.cs +++ b/src/LeanCode.ContractsGenerator/Generation/IEnumerableExtensions.cs @@ -4,8 +4,5 @@ namespace LeanCode.ContractsGenerator.Generation; internal static class IEnumerableExtensions { - public static void SaveToRepeatedField(this IEnumerable src, RepeatedField output) - { - output.AddRange(src); - } + public static void SaveToRepeatedField(this IEnumerable src, RepeatedField output) => output.AddRange(src); } diff --git a/src/LeanCode.ContractsGenerator/Generation/NotificationTagGenerator.cs b/src/LeanCode.ContractsGenerator/Generation/NotificationTagGenerator.cs index a829ad3..31b9079 100644 --- a/src/LeanCode.ContractsGenerator/Generation/NotificationTagGenerator.cs +++ b/src/LeanCode.ContractsGenerator/Generation/NotificationTagGenerator.cs @@ -1,5 +1,3 @@ -using System.Text; - namespace LeanCode.ContractsGenerator.Generation; public static class NotificationTagGenerator @@ -10,40 +8,16 @@ public static class NotificationTagGenerator /// `LeanCode.Contracts.NotificationTagGenerator` that generates a tag based on `Type`. /// Both methods generate the same tags. /// - public static string Generate(TypeRef typeRef) - { - return typeRef switch + public static string Generate(TypeRef typeRef) => + typeRef switch { { Internal: TypeRef.Types.Internal i } => $"{i.Name}{GetArgumentsString(i.Arguments)}", { Generic: TypeRef.Types.Generic g } => g.Name, { Known: TypeRef.Types.Known k } => - $"{LeanCode.Contracts.NotificationTagGenerator.KnownTypePrefix}{k.Type}{GetArgumentsString(k.Arguments)}", + $"{Contracts.NotificationTagGenerator.KnownTypePrefix}{k.Type}{GetArgumentsString(k.Arguments)}", _ => throw new InvalidOperationException($"Unknown TypeRef: {typeRef}."), }; - } - - private static string GetArgumentsString(IEnumerable args) - { - var argsBuilder = new StringBuilder(); - - if (args.Any()) - { - argsBuilder.Append('['); - - foreach (var arg in args) - { - if (argsBuilder.Length > 1) - { - argsBuilder.Append(','); - } - - var argName = Generate(arg); - argsBuilder.Append(argName); - } - - argsBuilder.Append(']'); - } - return argsBuilder.ToString(); - } + private static string GetArgumentsString(IEnumerable args) => + args.Any() ? $"[{string.Join(',', args.Select(Generate))}]" : string.Empty; } diff --git a/src/LeanCode.ContractsGenerator/Generation/ObjectExtensions.cs b/src/LeanCode.ContractsGenerator/Generation/ObjectExtensions.cs index e70e82e..1b71b87 100644 --- a/src/LeanCode.ContractsGenerator/Generation/ObjectExtensions.cs +++ b/src/LeanCode.ContractsGenerator/Generation/ObjectExtensions.cs @@ -2,9 +2,8 @@ namespace LeanCode.ContractsGenerator.Generation; public static class ObjectExtensions { - public static ValueRef ToValueRef(this object? val) - { - return val switch + public static ValueRef ToValueRef(this object? val) => + val switch { null => new() { Null = new() }, byte v => new ValueRef { Number = new() { Value = v } }, @@ -21,5 +20,4 @@ public static ValueRef ToValueRef(this object? val) bool v => new ValueRef { Bool = new() { Value = v } }, _ => throw new NotSupportedException($"Cannot generate contracts for constant of type {val.GetType()}."), }; - } } diff --git a/src/LeanCode.ContractsGenerator/Generation/TypeRefFactory.cs b/src/LeanCode.ContractsGenerator/Generation/TypeRefFactory.cs index e393c5e..f8cca0c 100644 --- a/src/LeanCode.ContractsGenerator/Generation/TypeRefFactory.cs +++ b/src/LeanCode.ContractsGenerator/Generation/TypeRefFactory.cs @@ -3,15 +3,8 @@ namespace LeanCode.ContractsGenerator.Generation; -public sealed class TypeRefFactory +public sealed class TypeRefFactory(CompiledContracts contracts) { - private readonly CompiledContracts contracts; - - public TypeRefFactory(CompiledContracts contracts) - { - this.contracts = contracts; - } - public TypeRef From(ITypeSymbol symbol) { var isNullable = IsNullable(symbol); @@ -119,7 +112,7 @@ when ts is INamedTypeSymbol ns && ns.Arity == 2 && ( contracts.Types.IsReadOnlyDictionary(ns) - || ns.Interfaces.Any(i => contracts.Types.IsReadOnlyDictionary(i)) + || ns.Interfaces.Any(contracts.Types.IsReadOnlyDictionary) ) => New(KnownType.Map, From(ns.TypeArguments[0]), From(ns.TypeArguments[1])), _ => null, @@ -133,8 +126,5 @@ static TypeRef.Types.Known New(KnownType type, params TypeRef[] args) } } - private static bool IsNullable(ITypeSymbol symbol) - { - return symbol.NullableAnnotation == NullableAnnotation.Annotated; - } + private static bool IsNullable(ITypeSymbol symbol) => symbol.NullableAnnotation == NullableAnnotation.Annotated; } diff --git a/src/LeanCode.ContractsGenerator/IAnalyzer.cs b/src/LeanCode.ContractsGenerator/IAnalyzer.cs index 6de2a9c..1dee2c9 100644 --- a/src/LeanCode.ContractsGenerator/IAnalyzer.cs +++ b/src/LeanCode.ContractsGenerator/IAnalyzer.cs @@ -2,7 +2,7 @@ namespace LeanCode.ContractsGenerator; public interface IAnalyzer { - IEnumerable Analyze(Export export); + public IEnumerable Analyze(Export export); } public record AnalyzeError(string Code, string Message, AnalyzerContext Context) diff --git a/src/LeanCode.ContractsGenerator/LeanCode.ContractsGenerator.csproj b/src/LeanCode.ContractsGenerator/LeanCode.ContractsGenerator.csproj index 909b53c..7f7df07 100644 --- a/src/LeanCode.ContractsGenerator/LeanCode.ContractsGenerator.csproj +++ b/src/LeanCode.ContractsGenerator/LeanCode.ContractsGenerator.csproj @@ -31,6 +31,7 @@ + diff --git a/src/LeanCode.ContractsGenerator/Program.cs b/src/LeanCode.ContractsGenerator/Program.cs index af8bdc4..79fb6a8 100644 --- a/src/LeanCode.ContractsGenerator/Program.cs +++ b/src/LeanCode.ContractsGenerator/Program.cs @@ -2,7 +2,6 @@ using Google.Protobuf; using LeanCode.ContractsGenerator.Compilation; using LeanCode.ContractsGenerator.Generation; -using Microsoft.CodeAnalysis; using Microsoft.Extensions.FileSystemGlobbing; namespace LeanCode.ContractsGenerator; @@ -44,7 +43,7 @@ public class ProjectOptions : IOptions MetaValue = "FILE", HelpText = "The project file with contracts. To pass multiple projects, separate the values with space." )] - public IEnumerable ProjectFiles { get; set; } = Array.Empty(); + public IEnumerable ProjectFiles { get; set; } = []; } [Verb("file", HelpText = "Generate contracts from a single file.")] @@ -70,7 +69,7 @@ public class PathOptions : IOptions MetaValue = "PATTERN", HelpText = "Include files from glob pattern. To pass multiple patterns, separate them with space." )] - public IEnumerable Include { get; set; } = Array.Empty(); + public IEnumerable Include { get; set; } = []; [Option( 'e', @@ -79,7 +78,7 @@ public class PathOptions : IOptions MetaValue = "PATTERN", HelpText = "Exclude files from glob pattern. Has higher precedence than includes. To pass multiple patterns, separate them with space." )] - public IEnumerable Exclude { get; set; } = Array.Empty(); + public IEnumerable Exclude { get; set; } = []; [Option( 'd', @@ -227,33 +226,8 @@ private static async Task WriteToFileAsync(Export generated, string filepath) private static async Task WriteToStdoutAsync(Export generated) { - await using var outputStream = System.Console.OpenStandardOutput(); + await using var outputStream = Console.OpenStandardOutput(); using var codedOutput = new CodedOutputStream(outputStream, true); generated.WriteTo(codedOutput); } - - private static string FormatLocation(Location location) - { - var lineSpan = location.GetMappedLineSpan(); - if (lineSpan.Path is not null) - { - return lineSpan.Path - + "@" - + (lineSpan.StartLinePosition.Line + 1) - + ":" - + (lineSpan.StartLinePosition.Character + 1); - } - else if (location.IsInSource) - { - return location.Kind + "(" + location.SourceTree?.FilePath + location.SourceSpan.ToString() + ")"; - } - else if (location.IsInMetadata && location.MetadataModule is not null) - { - return location.Kind + "(" + location.MetadataModule.Name + ")"; - } - else - { - return location.Kind.ToString(); - } - } }