From ae32c2ae7d1cd81552a08b0b53945c9381258cf9 Mon Sep 17 00:00:00 2001 From: rameel Date: Tue, 29 Sep 2026 19:03:01 +0500 Subject: [PATCH] build: enable analyzers and treat warnings as errors --- .editorconfig | 72 +++---------------- Directory.Build.props | 15 ++++ Ramstack.HtmxToolkit.slnx | 1 + .../Properties/JetBrains.Annotations.cs | 4 ++ .../Ramstack.HtmxToolkit.csproj | 5 -- .../Collections/SmallDictionaryTests.cs | 6 +- .../HtmxFieldValuesTests.cs | 3 + .../HtmxResponseTests.cs | 2 +- .../Ramstack.HtmxToolkit.Tests.csproj | 1 + .../TagHelpers/HtmxConfigTagHelperTests.cs | 18 ++--- 10 files changed, 48 insertions(+), 79 deletions(-) create mode 100644 Directory.Build.props diff --git a/.editorconfig b/.editorconfig index c60bc65..caaa5b4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,8 +4,9 @@ root = true charset = utf-8 indent_size = 4 indent_style = space -insert_final_newline = true trim_trailing_whitespace = true +insert_final_newline = true +resharper_csharp_max_line_length = 160 [*.{cshtml,css,yml,props,targets,csproj}] indent_size = 2 @@ -13,68 +14,15 @@ indent_size = 2 [*.min.js] insert_final_newline = false -[*.{cs,cshtml}] -resharper_csharp_max_line_length = 160 - +[*.cs] # IDE0079: Remove unnecessary suppression dotnet_remove_unnecessary_suppression_exclusions = category:ReSharper -# IDE0079: Remove unnecessary suppression -# dotnet_diagnostic.IDE0079.severity = none - -# CA2255: The 'ModuleInitializer' attribute should not be used in libraries -dotnet_diagnostic.CA2255.severity = none - -# CS8600: Converting null literal or possible null value to non-nullable type. -dotnet_diagnostic.CS8600.severity = error - -# CS8601: Possible null reference assignment. -dotnet_diagnostic.CS8601.severity = error - -# CS8602: Dereference of a possibly null reference. -dotnet_diagnostic.CS8602.severity = error - -# CS8603: Possible null reference return. -dotnet_diagnostic.CS8603.severity = error - -# CS8604: Possible null reference argument. -dotnet_diagnostic.CS8604.severity = error - -# CS8610: Nullability of reference types in type of parameter doesn't match overridden member. -dotnet_diagnostic.CS8610.severity = error - -# CS8613: Nullability of reference types in return type doesn't match implicitly implemented member. -dotnet_diagnostic.CS8613.severity = error - -# CS8614: Nullability of reference types in type of parameter doesn't match implicitly implemented member. -dotnet_diagnostic.CS8614.severity = error - -# CS8618: Non-nullable field is uninitialized. Consider declaring as nullable. -dotnet_diagnostic.CS8618.severity = error - -# CS8619: Nullability of reference types in value doesn't match target type. -dotnet_diagnostic.CS8619.severity = error - -# CS8620: Argument cannot be used for parameter due to differences in the nullability of reference types. -dotnet_diagnostic.CS8620.severity = error - -# CS8625: Cannot convert null literal to non-nullable reference type. -dotnet_diagnostic.CS8625.severity = error - -# CS8634: The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'class' constraint. -dotnet_diagnostic.CS8634.severity = error - -# CS8643: Nullability of reference types in explicit interface specifier doesn't match interface implemented by the type. -dotnet_diagnostic.CS8643.severity = error - -# CS8653: A default expression introduces a null value for a type parameter. -dotnet_diagnostic.CS8653.severity = error - -# CS8714: The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. -dotnet_diagnostic.CS8714.severity = error - -# IDE0059: Unnecessary assignment of a value -dotnet_diagnostic.IDE0059.severity = none +[tests/**.cs] +# CA1707: Identifiers should not contain underscores +# Tests follow the Method_Condition_ExpectedResult naming convention. +dotnet_diagnostic.CA1707.severity = none -# IDE0060: Remove unused parameter -dotnet_diagnostic.IDE0060.severity = none +# CA1859: Use concrete types when possible for improved performance +# Tests exercise interface contracts and use abstract ASP.NET Core types. +dotnet_diagnostic.CA1859.severity = none diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..2055961 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,15 @@ + + + $(NoWarn);CS0649 + true + + + + true + latest + Recommended + Default + true + true + + diff --git a/Ramstack.HtmxToolkit.slnx b/Ramstack.HtmxToolkit.slnx index 92f9a49..a07a81a 100644 --- a/Ramstack.HtmxToolkit.slnx +++ b/Ramstack.HtmxToolkit.slnx @@ -3,6 +3,7 @@ + diff --git a/src/Ramstack.HtmxToolkit/Properties/JetBrains.Annotations.cs b/src/Ramstack.HtmxToolkit/Properties/JetBrains.Annotations.cs index 2e16b23..5b3af51 100644 --- a/src/Ramstack.HtmxToolkit/Properties/JetBrains.Annotations.cs +++ b/src/Ramstack.HtmxToolkit/Properties/JetBrains.Annotations.cs @@ -32,6 +32,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // ReSharper disable CheckNamespace // ReSharper disable ConvertToPrimaryConstructor +using System.Diagnostics.CodeAnalysis; + namespace JetBrains.Annotations; /// @@ -163,6 +165,8 @@ internal enum ImplicitUseKindFlags /// with or . /// [Flags] +[SuppressMessage("Design", "CA1069:Enums values should not be duplicated", + Justification = "JetBrains annotations intentionally declare Default as an alias before Itself.")] internal enum ImplicitUseTargetFlags { Default = Itself, diff --git a/src/Ramstack.HtmxToolkit/Ramstack.HtmxToolkit.csproj b/src/Ramstack.HtmxToolkit/Ramstack.HtmxToolkit.csproj index fa92850..b5056b3 100644 --- a/src/Ramstack.HtmxToolkit/Ramstack.HtmxToolkit.csproj +++ b/src/Ramstack.HtmxToolkit/Ramstack.HtmxToolkit.csproj @@ -10,11 +10,6 @@ true - - 649;1591 - CS8600;CS8601;CS8602;CS8603;CS8604;CS8610;CS8613;CS8614;CS8618;CS8619;CS8620;CS8625;CS8643;CS8653;CS8714 - - https://github.com/rameel/ramstack.htmxtoolkit Rameel diff --git a/tests/Ramstack.HtmxToolkit.Tests/Collections/SmallDictionaryTests.cs b/tests/Ramstack.HtmxToolkit.Tests/Collections/SmallDictionaryTests.cs index ff25752..17ac724 100644 --- a/tests/Ramstack.HtmxToolkit.Tests/Collections/SmallDictionaryTests.cs +++ b/tests/Ramstack.HtmxToolkit.Tests/Collections/SmallDictionaryTests.cs @@ -100,14 +100,14 @@ public void Constructor_FromSmallDictionary_WithDifferentComparer_ReSortsEntries public void Constructor_NullCollection_ThrowsArgumentNullException() { Assert.Throws( - () => new SmallDictionary(null!, Comparer.Default)); + () => _ = new SmallDictionary(null!, Comparer.Default)); } [Test] public void Constructor_NullComparer_ThrowsArgumentNullException() { Assert.Throws( - () => new SmallDictionary([], null!)); + () => _ = new SmallDictionary([], null!)); } [Test] @@ -642,6 +642,8 @@ public void Enumerators_Reset_ThrowsNotSupportedException() } [Test] + [SuppressMessage("Performance", "CA1841:Prefer dictionary Contains methods", + Justification = "The test exercises the Keys collection's Contains implementation.")] public void ConfiguredComparer_AllOperations_UseComparerForKeyIdentity() { IDictionary dictionary = new SmallDictionary(StringComparer.OrdinalIgnoreCase); diff --git a/tests/Ramstack.HtmxToolkit.Tests/HtmxFieldValuesTests.cs b/tests/Ramstack.HtmxToolkit.Tests/HtmxFieldValuesTests.cs index caee480..92ac9f7 100644 --- a/tests/Ramstack.HtmxToolkit.Tests/HtmxFieldValuesTests.cs +++ b/tests/Ramstack.HtmxToolkit.Tests/HtmxFieldValuesTests.cs @@ -60,6 +60,9 @@ public void Constructor_Array_StoresArrayWithoutCopying() } [Test] + [SuppressMessage("ReSharper", "UseArrayEmptyMethod")] + [SuppressMessage("Performance", "CA1825:Avoid zero-length array allocations", + Justification = "A distinct empty array verifies that the constructor preserves reference identity.")] public void Constructor_EmptyArray_PreservesArrayRepresentation() { var source = new string [0]; diff --git a/tests/Ramstack.HtmxToolkit.Tests/HtmxResponseTests.cs b/tests/Ramstack.HtmxToolkit.Tests/HtmxResponseTests.cs index 2b90523..3788ee1 100644 --- a/tests/Ramstack.HtmxToolkit.Tests/HtmxResponseTests.cs +++ b/tests/Ramstack.HtmxToolkit.Tests/HtmxResponseTests.cs @@ -322,7 +322,7 @@ public void TriggerEvent_Htmx4_AddsEveryTimingToReceiveTrigger() [JsonSourceGenerationOptions(WriteIndented = true, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] [JsonSerializable(typeof(TriggerEventDetail))] -internal partial class TriggerEventJsonSerializerContext : JsonSerializerContext; +internal sealed partial class TriggerEventJsonSerializerContext : JsonSerializerContext; internal sealed class TriggerEventDetail { diff --git a/tests/Ramstack.HtmxToolkit.Tests/Ramstack.HtmxToolkit.Tests.csproj b/tests/Ramstack.HtmxToolkit.Tests/Ramstack.HtmxToolkit.Tests.csproj index d277217..023e9b0 100644 --- a/tests/Ramstack.HtmxToolkit.Tests/Ramstack.HtmxToolkit.Tests.csproj +++ b/tests/Ramstack.HtmxToolkit.Tests/Ramstack.HtmxToolkit.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/tests/Ramstack.HtmxToolkit.Tests/TagHelpers/HtmxConfigTagHelperTests.cs b/tests/Ramstack.HtmxToolkit.Tests/TagHelpers/HtmxConfigTagHelperTests.cs index 73f419f..4bdd3c8 100644 --- a/tests/Ramstack.HtmxToolkit.Tests/TagHelpers/HtmxConfigTagHelperTests.cs +++ b/tests/Ramstack.HtmxToolkit.Tests/TagHelpers/HtmxConfigTagHelperTests.cs @@ -151,8 +151,8 @@ public async Task ProcessAsync_SerializesOnlyHtmx1Options() var json = await RenderJson(options); - Assert.That(json.Keys, Is.EquivalentTo(new[] - { + Assert.That(json.Keys, Is.EquivalentTo( + [ "historyEnabled", "historyCacheSize", "refreshOnHistoryMiss", "defaultSwapStyle", "defaultSwapDelay", "defaultSettleDelay", "includeIndicatorStyles", "indicatorClass", "requestClass", "addedClass", "swappingClass", "settlingClass", "allowEval", @@ -161,7 +161,7 @@ public async Task ProcessAsync_SerializesOnlyHtmx1Options() "selfRequestsOnly", "scrollBehavior", "defaultFocusScroll", "getCacheBusterParam", "globalViewTransitions", "methodsThatUseUrlParams", "ignoreTitle", "scrollIntoViewOnBoost", "triggerSpecsCache" - })); + ])); Assert.That(json["defaultSwapStyle"].GetString(), Is.EqualTo("outerHTML")); Assert.That(json["wsBinaryType"].GetString(), Is.EqualTo("arraybuffer")); Assert.That(json["scrollBehavior"].GetString(), Is.EqualTo("smooth")); @@ -219,8 +219,8 @@ public async Task ProcessAsync_SerializesOnlyHtmx2Options_InResponseHandlingOrde var json = await RenderJson(options); - Assert.That(json.Keys, Is.EquivalentTo(new[] - { + Assert.That(json.Keys, Is.EquivalentTo( + [ "historyEnabled", "historyCacheSize", "refreshOnHistoryMiss", "defaultSwapStyle", "defaultSwapDelay", "defaultSettleDelay", "includeIndicatorStyles", "indicatorClass", "requestClass", "addedClass", "swappingClass", "settlingClass", "allowEval", @@ -230,7 +230,7 @@ public async Task ProcessAsync_SerializesOnlyHtmx2Options_InResponseHandlingOrde "getCacheBusterParam", "globalViewTransitions", "methodsThatUseUrlParams", "ignoreTitle", "scrollIntoViewOnBoost", "triggerSpecsCache", "responseHandling", "allowNestedOobSwaps", "historyRestoreAsHxRequest", "reportValidityOfForms" - })); + ])); Assert.That(json["defaultSwapStyle"].GetString(), Is.EqualTo("innerHTML")); Assert.That(json["scrollBehavior"].GetString(), Is.EqualTo("instant")); @@ -270,14 +270,14 @@ public async Task ProcessAsync_SerializesOnlyHtmx4Options() var json = await RenderJson(options); - Assert.That(json.Keys, Is.EquivalentTo(new[] - { + Assert.That(json.Keys, Is.EquivalentTo( + [ "logAll", "prefix", "history", "defaultSwap", "allowEmptySwapAfterOOB", "defaultSettleDelay", "includeIndicatorCSS", "indicatorClass", "requestClass", "inlineScriptNonce", "extensions", "implicitInheritance", "defaultTimeout", "mode", "defaultFocusScroll", "transitions", "morphIgnore", "morphSkip", "morphSkipChildren", "morphScanLimit", "noSwap" - })); + ])); Assert.That(json["history"].GetString(), Is.EqualTo("reload")); Assert.That(json["defaultSwap"].GetString(), Is.EqualTo("outerMorph")); Assert.That(json["mode"].GetString(), Is.EqualTo("no-cors"));