diff --git a/Code/Light.GuardClauses.AllProjects.sln.DotSettings b/Code/Light.GuardClauses.AllProjects.sln.DotSettings index 5c674a6..2c4519b 100644 --- a/Code/Light.GuardClauses.AllProjects.sln.DotSettings +++ b/Code/Light.GuardClauses.AllProjects.sln.DotSettings @@ -249,6 +249,7 @@ True True True + True True True True diff --git a/Code/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj b/Code/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj index 814ef38..c15326f 100644 --- a/Code/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj +++ b/Code/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj @@ -1,15 +1,15 @@  - net6.0 + net8.0 - - - - + + + + diff --git a/Code/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj b/Code/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj index 5ce7f44..1df3b66 100644 --- a/Code/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj +++ b/Code/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj @@ -2,10 +2,11 @@ netstandard2.0 + true - + \ No newline at end of file diff --git a/Code/Light.GuardClauses.InternalRoslynAnalyzers/XmlCommentAnalyzer.cs b/Code/Light.GuardClauses.InternalRoslynAnalyzers/XmlCommentAnalyzer.cs index c96ef39..ec3d601 100644 --- a/Code/Light.GuardClauses.InternalRoslynAnalyzers/XmlCommentAnalyzer.cs +++ b/Code/Light.GuardClauses.InternalRoslynAnalyzers/XmlCommentAnalyzer.cs @@ -37,7 +37,7 @@ private static void AnalyzeXmlCommentsForParameterNameAndMessage(SymbolAnalysisC if (methodSymbol.DeclaringSyntaxReferences.Length != 1 || !(methodSymbol.DeclaringSyntaxReferences[0].GetSyntax() is MethodDeclarationSyntax methodDeclarationSyntax) || !(methodDeclarationSyntax.DescendantTrivia() - .SingleOrDefault(trivia => trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia) + .SingleOrDefault(trivia => trivia.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)) .GetStructure() is DocumentationCommentTriviaSyntax documentationSyntax)) return; diff --git a/Code/Light.GuardClauses.Performance/StringAssertions/MustBeBenchmark.cs b/Code/Light.GuardClauses.Performance/StringAssertions/MustBeBenchmark.cs index 62310d7..19f51c5 100644 --- a/Code/Light.GuardClauses.Performance/StringAssertions/MustBeBenchmark.cs +++ b/Code/Light.GuardClauses.Performance/StringAssertions/MustBeBenchmark.cs @@ -22,6 +22,6 @@ public string ImperativeVersion() [Benchmark] public string LightGuardClausesCustomException() => - X.MustBe(Y, StringComparisonType.OrdinalIgnoreWhiteSpace, (x, y) => new Exception("The strings are not equal.")); + X.MustBe(Y, StringComparisonType.OrdinalIgnoreWhiteSpace, (_, _, _) => new Exception("The strings are not equal.")); } } \ No newline at end of file diff --git a/Code/Light.GuardClauses.Performance/StringAssertions/MustNotBeBenchmark.cs b/Code/Light.GuardClauses.Performance/StringAssertions/MustNotBeBenchmark.cs index 67f3c3c..3b5da46 100644 --- a/Code/Light.GuardClauses.Performance/StringAssertions/MustNotBeBenchmark.cs +++ b/Code/Light.GuardClauses.Performance/StringAssertions/MustNotBeBenchmark.cs @@ -25,6 +25,6 @@ public string ImperativeVersion() [Benchmark] public string LightGuardClausesCustomException() => - X.MustNotBe(Y, StringComparisonType.OrdinalIgnoreCaseIgnoreWhiteSpace, (x, y) => new Exception("The strings are equal.")); + X.MustNotBe(Y, StringComparisonType.OrdinalIgnoreCaseIgnoreWhiteSpace, (_, _, _) => new Exception("The strings are equal.")); } } \ No newline at end of file diff --git a/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj b/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj index b30140e..229eeab 100644 --- a/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj +++ b/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj @@ -1,8 +1,8 @@  - netstandard2.0 - 10.0 + netstandard2.0 + 12.0 Kenny Pflug Kenny Pflug Copyright © Kenny Pflug 2016 - 2023 diff --git a/Code/Light.GuardClauses.SourceCodeTransformation.Tests/Light.GuardClauses.SourceCodeTransformation.Tests.csproj b/Code/Light.GuardClauses.SourceCodeTransformation.Tests/Light.GuardClauses.SourceCodeTransformation.Tests.csproj index 9a4eeec..75dd49f 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation.Tests/Light.GuardClauses.SourceCodeTransformation.Tests.csproj +++ b/Code/Light.GuardClauses.SourceCodeTransformation.Tests/Light.GuardClauses.SourceCodeTransformation.Tests.csproj @@ -6,11 +6,11 @@ - - - - - + + + + + \ No newline at end of file diff --git a/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj b/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj index de93906..f9959ae 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj +++ b/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj @@ -4,16 +4,16 @@ Exe - net7.0 + net8.0 enable - - - - + + + + diff --git a/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs b/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs index 78f6bba..6ceb25d 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs +++ b/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs @@ -34,7 +34,7 @@ public static void CreateSingleSourceFile(SourceFileMergeOptions options) .AppendLine($@"License information for Light.GuardClauses The MIT License (MIT) -Copyright (c) 2016, 2023 Kenny Pflug mailto:kenny.pflug@live.de +Copyright (c) 2016, 2024 Kenny Pflug mailto:kenny.pflug@live.de Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal @@ -69,6 +69,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System.Text.RegularExpressions; {(options.IncludeJetBrainsAnnotationsUsing ? "using JetBrains.Annotations;" + Environment.NewLine : string.Empty)}using {options.BaseNamespace}.Exceptions; using {options.BaseNamespace}.FrameworkExtensions; +{(options.IncludeJetBrainsAnnotationsUsing ? "using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute;" : "")} #nullable enable annotations diff --git a/Code/Light.GuardClauses.Tests/ComparableAssertions/RangeTests.cs b/Code/Light.GuardClauses.Tests/ComparableAssertions/RangeTests.cs index 3b81b9f..e2a848b 100644 --- a/Code/Light.GuardClauses.Tests/ComparableAssertions/RangeTests.cs +++ b/Code/Light.GuardClauses.Tests/ComparableAssertions/RangeTests.cs @@ -55,6 +55,78 @@ public static void ConstructorException(int from, int to) .Message.Should().Contain($"{nameof(to)} must not be less than {from}, but it actually is {to}."); } + [Theory] + [InlineData(0, 10, 0)] // Lower boundary + [InlineData(0, 10, 5)] // Middle value + [InlineData(0, 10, 10)] // Upper boundary + public static void InclusiveBetween_ValuesInRange(int from, int to, int value) + { + var range = Range.InclusiveBetween(from, to); + + var result = range.IsValueWithinRange(value); + + result.Should().BeTrue(); + } + + [Theory] + [InlineData(0, 10, -1)] // Below range + [InlineData(0, 10, 11)] // Above range + public static void InclusiveBetween_ValuesOutOfRange(int from, int to, int value) + { + var range = Range.InclusiveBetween(from, to); + + var result = range.IsValueWithinRange(value); + + result.Should().BeFalse(); + } + + [Theory] + [InlineData(0, 10, 1)] // Just above lower boundary + [InlineData(0, 10, 5)] // Middle value + [InlineData(0, 10, 9)] // Just below upper boundary + public static void ExclusiveBetween_ValuesInRange(int from, int to, int value) + { + var range = Range.ExclusiveBetween(from, to); + + var result = range.IsValueWithinRange(value); + + result.Should().BeTrue(); + } + + [Theory] + [InlineData(0, 10, 0)] // Lower boundary + [InlineData(0, 10, 10)] // Upper boundary + [InlineData(0, 10, -1)] // Below range + [InlineData(0, 10, 11)] // Above range + public static void ExclusiveBetween_ValuesOutOfRange(int from, int to, int value) + { + var range = Range.ExclusiveBetween(from, to); + + var result = range.IsValueWithinRange(value); + + result.Should().BeFalse(); + } + + [Theory] + [InlineData(10, 5)] // To less than From + [InlineData(-5, -10)] // Negative To less than From + public static void InclusiveBetween_InvalidRange_ThrowsException(int from, int to) + { + Action inclusiveAct = () => Range.InclusiveBetween(from, to); + inclusiveAct.Should().Throw() + .And.Message.Should().Contain($"{nameof(to)} must not be less than {from}"); + } + + [Theory] + [InlineData(10, 5)] // To less than From + [InlineData(-5, -10)] // Negative To less than From + public static void ExclusiveBetween_InvalidRange_ThrowsException(int from, int to) + { + Action exclusiveAct = () => Range.ExclusiveBetween(from, to); + exclusiveAct.Should().Throw() + .And.Message.Should().Contain($"{nameof(to)} must not be less than {from}"); + } + [Theory] [MemberData(nameof(Collections))] public static void RangeForCollections(IEnumerable enumerable) @@ -164,4 +236,4 @@ public static void RangeForReadOnlySpan(Memory memory) Enumerable.Range(1, 500).ToArray(), Array.Empty() }; -} \ No newline at end of file +} diff --git a/Code/Light.GuardClauses.Tests/Issues/Issue72NotNullAttributeTests.cs b/Code/Light.GuardClauses.Tests/Issues/Issue72NotNullAttributeTests.cs new file mode 100644 index 0000000..c6c0123 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/Issues/Issue72NotNullAttributeTests.cs @@ -0,0 +1,1520 @@ +#nullable enable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq.Expressions; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using FluentAssertions; +using Light.GuardClauses.FrameworkExtensions; +using Xunit; + +namespace Light.GuardClauses.Tests.Issues; + +// This occurs for delegates that create exceptions, and we just turn it off for these tests +#pragma warning disable CS8622 // Nullability of reference types in type of parameter doesn't match the target delegate type. + +public static class Issue72NotNullAttributeTests +{ + [Fact] + public static void CheckMustNotBeNull() + { + TestMustNotBeNull("foo").Should().Be(3); + _ = TestMustNotBeNullWithDelegate("foo").Should().Be(3); + return; + + static int TestMustNotBeNull(string? input) + { + Check.MustNotBeNull(input); + return input.Length; + } + + static int TestMustNotBeNullWithDelegate(string? input) + { + input.MustNotBeNull(() => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustNotBeDefault() + { + TestMustNotBeDefault("foo").Should().Be(3); + TestMustNotBeDefaultWithDelegate("foo").Should().Be(3); + return; + + static int TestMustNotBeDefault(string? input) + { + input.MustNotBeDefault(); + return input.Length; + } + + static int TestMustNotBeDefaultWithDelegate(string? input) + { + input.MustNotBeDefault(() => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustNotBeNullReference() + { + TestMustNotBeNullReference("foo").Should().Be(3); + TestMustNotBeNullReferenceWithDelegate("foo").Should().Be(3); + return; + + static int TestMustNotBeNullReference(string? input) + { + input.MustNotBeNullReference(); + return input.Length; + } + + static int TestMustNotBeNullReferenceWithDelegate(string? input) + { + input.MustNotBeNullReference(() => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustBeOfType() + { + TestMustBeOfType("foo").Should().Be(3); + TestMustBeOfTypeWithDelegate("foo").Should().Be(3); + return; + + static int TestMustBeOfType(object? input) + { + input.MustBeOfType(); + return ((string) input).Length; + } + + static int TestMustBeOfTypeWithDelegate(object? input) + { + input.MustBeOfType(_ => new Exception()); + return ((string) input).Length; + } + } + + [Fact] + public static void CheckMustHaveValue() + { + TestMustHaveValue(42).Should().Be(42); + TestMustHaveValueWithDelegate(42).Should().Be(42); + return; + + static int TestMustHaveValue(int? input) + { + input.MustHaveValue(); + return input.Value; + } + + static int TestMustHaveValueWithDelegate(int? input) + { + input.MustHaveValue(() => new Exception()); + return input.Value; + } + } + + [Fact] + public static void CheckMustNotBeLessThan() + { + TestMustNotBeLessThan("foo").Should().Be("foo"); + TestMustNotBeLessThanWithDelegate("foo").Should().Be("foo"); + return; + + // I need to provoke a compiler warning +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static string TestMustNotBeLessThan(string? input) + { + input.MustNotBeLessThan("bar"); + return input; + } + + static string TestMustNotBeLessThanWithDelegate(string? input) + { + input.MustNotBeLessThan("bar", (_, _) => new Exception()); + return input; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustBeGreaterThanOrEqualTo() + { + TestMustBeGreaterThanOrEqualTo("foo").Should().Be("foo"); + TestMustBeGreaterThanOrEqualToWithDelegate("foo").Should().Be("foo"); + return; + +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static string TestMustBeGreaterThanOrEqualTo(string? input) + { + input.MustBeGreaterThanOrEqualTo("bar"); + return input; + } + + static string TestMustBeGreaterThanOrEqualToWithDelegate(string? input) + { + input.MustBeGreaterThanOrEqualTo("bar", (_, _) => new Exception()); + return input; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustBeLessThan() + { + TestMustBeLessThan("bar").Should().Be("bar"); + TestMustBeLessThanWithDelegate("bar").Should().Be("bar"); + return; + +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static string TestMustBeLessThan(string? input) + { + input.MustBeLessThan("foo"); + return input; + } + + static string TestMustBeLessThanWithDelegate(string? input) + { + input.MustBeLessThan("foo", (_, _) => new Exception()); + return input; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustNotBeGreaterThanOrEqualTo() + { + TestMustNotBeGreaterThanOrEqualTo("bar").Should().Be("bar"); + TestMustNotBeGreaterThanOrEqualToWithDelegate("bar").Should().Be("bar"); + return; + +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static string TestMustNotBeGreaterThanOrEqualTo(string? input) + { + input.MustNotBeGreaterThanOrEqualTo("foo"); + return input; + } + + static string TestMustNotBeGreaterThanOrEqualToWithDelegate(string? input) + { + input.MustNotBeGreaterThanOrEqualTo("foo", (_, _) => new Exception()); + return input; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustBeGreaterThan() + { + TestMustBeGreaterThan("foo").Should().Be("foo"); + TestMustBeGreaterThanWithDelegate("foo").Should().Be("foo"); + return; + +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static string TestMustBeGreaterThan(string? input) + { + input.MustBeGreaterThan("bar"); + return input; + } + + static string TestMustBeGreaterThanWithDelegate(string? input) + { + input.MustBeGreaterThan("bar", (_, _) => new Exception()); + return input; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustNotBeLessThanOrEqualTo() + { + TestMustNotBeLessThanOrEqualTo("foo").Should().Be("foo"); + TestMustNotBeLessThanOrEqualToWithDelegate("foo").Should().Be("foo"); + return; + +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static string TestMustNotBeLessThanOrEqualTo(string? input) + { + input.MustNotBeLessThanOrEqualTo("bar"); + return input; + } + + static string TestMustNotBeLessThanOrEqualToWithDelegate(string? input) + { + input.MustNotBeLessThanOrEqualTo("bar", (_, _) => new Exception()); + return input; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustNotBeGreaterThan() + { + TestMustNotBeGreaterThan("bar").Should().Be("bar"); + TestMustNotBeGreaterThanWithDelegate("bar").Should().Be("bar"); + return; + +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static string TestMustNotBeGreaterThan(string? input) + { + input.MustNotBeGreaterThan("foo"); + return input; + } + + static string TestMustNotBeGreaterThanWithDelegate(string? input) + { + input.MustNotBeGreaterThan("foo", (_, _) => new Exception()); + return input; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustBeLessThanOrEqualTo() + { + TestMustBeLessThanOrEqualTo("bar").Should().Be("bar"); + TestMustBeLessThanOrEqualToWithDelegate("bar").Should().Be("bar"); + return; + +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static string TestMustBeLessThanOrEqualTo(string? input) + { + input.MustBeLessThanOrEqualTo("foo"); + return input; + } + + static string TestMustBeLessThanOrEqualToWithDelegate(string? input) + { + input.MustBeLessThanOrEqualTo("foo", (_, _) => new Exception()); + return input; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustBeIn() + { + TestMustBeIn("b").Should().Be(1); + TestMustBeInWithDelegate("b").Should().Be(1); + +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static int TestMustBeIn(string? input) + { + input.MustBeIn(Range.FromInclusive("a").ToInclusive("c")!); + return input.Length; + } + + static int TestMustBeInWithDelegate(string? input) + { + input.MustBeIn(Range.FromInclusive("a").ToInclusive("c")!, (_, _) => new Exception()); + return input.Length; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustNotBeIn() + { + TestMustNotBeIn("d").Should().Be(1); + TestMustNotBeInWithDelegate("d").Should().Be(1); + +#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + static int TestMustNotBeIn(string? input) + { + input.MustNotBeIn(Range.FromInclusive("a").ToInclusive("c")!); + return input.Length; + } + + static int TestMustNotBeInWithDelegate(string? input) + { + input.MustNotBeIn(Range.FromInclusive("a").ToInclusive("c")!, (_, _) => new Exception()); + return input.Length; + } +#pragma warning restore CS8631 + } + + [Fact] + public static void CheckMustHaveCount() + { + TestMustHaveCount([]).Should().Be(0); + TestMustHaveCountWithDelegate([]).Should().Be(0); + return; + + static int TestMustHaveCount(string[]? input) + { + input.MustHaveCount(0); + return input.Length; + } + + static int TestMustHaveCountWithDelegate(string[]? input) + { + input.MustHaveCount(0, (_, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustNotBeNullOrEmpty() + { + TestMustNotBeNullOrEmpty([1, 2, 3]).Should().Be(3); + TestMustNotBeNullOrEmptyWithDelegate([1, 2, 3]).Should().Be(3); + return; + + static int TestMustNotBeNullOrEmpty(int[]? input) + { + input.MustNotBeNullOrEmpty(); + return input.Length; + } + + static int TestMustNotBeNullOrEmptyWithDelegate(int[]? input) + { + input.MustNotBeNullOrEmpty(_ => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustContainForCollections() + { + TestMustContain([1, 2, 3]).Should().Be(3); + TestMustContainWithDelegate([1, 2, 3]).Should().Be(3); + return; + + static int TestMustContain(int[]? input) + { + input.MustContain(2); + return input.Length; + } + + static int TestMustContainWithDelegate(int[]? input) + { + input.MustContain(2, (_, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustNotContain() + { + TestMustNotContain([1, 2, 3]).Should().Be(3); + TestMustNotContainWithDelegate([1, 2, 3]).Should().Be(3); + return; + + static int TestMustNotContain(int[]? input) + { + input.MustNotContain(4); + return input.Length; + } + + static int TestMustNotContainWithDelegate(int[]? input) + { + input.MustNotContain(4, (_, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckIsOneOf() + { + TestIsOneOf("foo").Should().Be(3); + return; + + static int TestIsOneOf(string? input) + { + 'o'.IsOneOf(input!); + return input.Length; + } + } + + [Fact] + public static void CheckMustBeOneOf() + { + TestMustBeOneOf("foo").Should().Be(3); + TestMustBeOneOfWithDelegate("foo").Should().Be(3); + return; + + static int TestMustBeOneOf(string? input) + { + 'o'.MustBeOneOf(input!); + return input.Length; + } + + static int TestMustBeOneOfWithDelegate(string? input) + { + 'o'.MustBeOneOf(input!, (_, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustNotBeOneOf() + { + TestMustNotBeOneOf("bar").Should().Be(3); + TestMustNotBeOneOfWithDelegate("bar").Should().Be(3); + return; + + static int TestMustNotBeOneOf(string? input) + { + 'o'.MustNotBeOneOf(input!); + return input.Length; + } + + static int TestMustNotBeOneOfWithDelegate(string? input) + { + 'o'.MustNotBeOneOf(input!, (_, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustHaveMinimumCount() + { + TestMustHaveMinimumCount([1, 2, 3]).Should().Be(3); + TestMustHaveMinimumCountWithDelegate([1, 2, 3]).Should().Be(3); + return; + + static int TestMustHaveMinimumCount(int[]? input) + { + input.MustHaveMinimumCount(2); + return input.Length; + } + + static int TestMustHaveMinimumCountWithDelegate(int[]? input) + { + input.MustHaveMinimumCount(2, (_, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustHaveMaximumCount() + { + TestMustHaveMaximumCount([1, 2, 3]).Should().Be(3); + TestMustHaveMaximumCountWithDelegate([1, 2, 3]).Should().Be(3); + return; + + static int TestMustHaveMaximumCount(int[]? input) + { + input.MustHaveMaximumCount(3); + return input.Length; + } + + static int TestMustHaveMaximumCountWithDelegate(int[]? input) + { + input.MustHaveMaximumCount(3, (_, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustNotBeNullOrEmptyString() + { + TestMustNotBeNullOrEmptyString("foo").Should().Be(3); + TestMustNotBeNullOrEmptyStringWithDelegate("foo").Should().Be(3); + return; + + static int TestMustNotBeNullOrEmptyString(string? input) + { + input.MustNotBeNullOrEmpty(); + return input.Length; + } + + static int TestMustNotBeNullOrEmptyStringWithDelegate(string? input) + { + input.MustNotBeNullOrEmpty(_ => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustNotBeNullOrWhiteSpace() + { + TestMustNotBeNullOrWhiteSpace("foo").Should().Be(3); + TestMustNotBeNullOrWhiteSpaceWithDelegate("foo").Should().Be(3); + return; + + static int TestMustNotBeNullOrWhiteSpace(string? input) + { + input.MustNotBeNullOrWhiteSpace(); + return input.Length; + } + + static int TestMustNotBeNullOrWhiteSpaceWithDelegate(string? input) + { + input.MustNotBeNullOrWhiteSpace(_ => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustMatch() + { + TestMustMatch("foo", "^f.*").Should().Be(3); + TestMustMatchWithDelegate("foo", "^f.*").Should().Be(3); + return; + + static int TestMustMatch(string? input, string pattern) + { + input.MustMatch(new Regex(pattern)); + return input.Length; + } + + static int TestMustMatchWithDelegate(string? input, string pattern) + { + input.MustMatch(new Regex(pattern), (_, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustContainForStrings() + { + TestMustContain("foo").Should().Be(3); + TestMustContainWithDelegate("foo").Should().Be(3); + TestMustContainWithStringComparison("foo").Should().Be(3); + TestMustContainWithDelegateAndStringComparison("foo").Should().Be(3); + return; + + static int TestMustContain(string? input) + { + input.MustContain("oo"); + return input.Length; + } + + static int TestMustContainWithDelegate(string? input) + { + input.MustContain("oo", (_, _) => new Exception()); + return input.Length; + } + + static int TestMustContainWithStringComparison(string? input) + { + input.MustContain("OO", StringComparison.OrdinalIgnoreCase); + return input.Length; + } + + static int TestMustContainWithDelegateAndStringComparison(string? input) + { + input.MustContain("OO", StringComparison.OrdinalIgnoreCase, (_, _, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustNotContainForStrings() + { + TestMustNotContain("foo").Should().Be(3); + TestMustNotContainWithDelegate("foo").Should().Be(3); + TestMustNotContainWithStringComparison("foo").Should().Be(3); + TestMustNotContainWithDelegateAndStringComparison("foo").Should().Be(3); + return; + + static int TestMustNotContain(string? input) + { + input.MustNotContain("bar"); + return input.Length; + } + + static int TestMustNotContainWithDelegate(string? input) + { + input.MustNotContain("bar", (_, _) => new Exception()); + return input.Length; + } + + static int TestMustNotContainWithStringComparison(string? input) + { + input.MustNotContain("BAR", StringComparison.OrdinalIgnoreCase); + return input.Length; + } + + static int TestMustNotContainWithDelegateAndStringComparison(string? input) + { + input.MustNotContain("BAR", StringComparison.OrdinalIgnoreCase, (_, _, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustBeSubstringOf() + { + TestMustBeSubstringOf("foo", "foobar").Should().Be(3); + TestMustBeSubstringOfWithDelegate("foo", "foobar").Should().Be(3); + TestMustBeSubstringOfWithStringComparison("OO").Should().Be(2); + TestMustBeSubstringOfWithDelegateAndStringComparison("OO").Should().Be(2); + return; + + static int TestMustBeSubstringOf(string? input, string target) + { + input.MustBeSubstringOf(target); + return input.Length; + } + + static int TestMustBeSubstringOfWithDelegate(string? input, string target) + { + input.MustBeSubstringOf(target, (_, _) => new Exception()); + return input.Length; + } + + static int TestMustBeSubstringOfWithStringComparison(string? input) + { + input.MustBeSubstringOf("foo", StringComparison.OrdinalIgnoreCase); + return input.Length; + } + + static int TestMustBeSubstringOfWithDelegateAndStringComparison(string? input) + { + input.MustBeSubstringOf("foo", StringComparison.OrdinalIgnoreCase, (_, _, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustNotBeSubstringOf() + { + TestMustNotBeSubstringOf("foo", "bar").Should().Be(3); + TestMustNotBeSubstringOfWithDelegate("foo", "bar").Should().Be(3); + TestMustNotBeSubstringOfWithStringComparison("FOO").Should().Be(3); + TestMustNotBeSubstringOfWithDelegateAndStringComparison("FOO").Should().Be(3); + return; + + static int TestMustNotBeSubstringOf(string? input, string target) + { + input.MustNotBeSubstringOf(target); + return input.Length; + } + + static int TestMustNotBeSubstringOfWithDelegate(string? input, string target) + { + input.MustNotBeSubstringOf(target, (_, _) => new Exception()); + return input.Length; + } + + static int TestMustNotBeSubstringOfWithStringComparison(string? input) + { + input.MustNotBeSubstringOf("bar", StringComparison.OrdinalIgnoreCase); + return input.Length; + } + + static int TestMustNotBeSubstringOfWithDelegateAndStringComparison(string? input) + { + input.MustNotBeSubstringOf("bar", StringComparison.OrdinalIgnoreCase, (_, _, _) => new Exception()); + return input.Length; + } + } + + [Fact] + public static void CheckMustBeEmailAddress() + { + TestMustBeEmailAddress("test@example.com").Should().Be("test@example.com"); + TestMustBeEmailAddressWithDelegate("test@example.com").Should().Be("test@example.com"); + TestMustBeEmailAddressWithRegex("test@example.com").Should().Be("test@example.com"); + TestMustBeEmailAddressWithRegexAndDelegate("test@example.com").Should().Be("test@example.com"); + return; + + static string TestMustBeEmailAddress(string? input) + { + input.MustBeEmailAddress(); + return input; + } + + static string TestMustBeEmailAddressWithDelegate(string? input) + { + input.MustBeEmailAddress(_ => new Exception()); + return input; + } + + static string TestMustBeEmailAddressWithRegex(string? input) + { + input.MustBeEmailAddress(new Regex(@"^[^@\s]+@[^@\s]+\.[^@\s]+$")); + return input; + } + + static string TestMustBeEmailAddressWithRegexAndDelegate(string? input) + { + input.MustBeEmailAddress(new Regex(@"^[^@\s]+@[^@\s]+\.[^@\s]+$"), (_, _) => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeShorterThan() + { + TestMustBeShorterThan("foo").Should().Be("foo"); + TestMustBeShorterThanWithDelegate("foo").Should().Be("foo"); + return; + + static string TestMustBeShorterThan(string? input) + { + input.MustBeShorterThan(5); + return input; + } + + static string TestMustBeShorterThanWithDelegate(string? input) + { + input.MustBeShorterThan(5, (_, _) => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeShorterThanOrEqualTo() + { + TestMustBeShorterThanOrEqualTo("foo").Should().Be("foo"); + TestMustBeShorterThanOrEqualToWithDelegate("foo").Should().Be("foo"); + return; + + static string TestMustBeShorterThanOrEqualTo(string? input) + { + input.MustBeShorterThanOrEqualTo(3); + return input; + } + + static string TestMustBeShorterThanOrEqualToWithDelegate(string? input) + { + input.MustBeShorterThanOrEqualTo(3, (_, _) => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustHaveLength() + { + TestMustHaveLength("foo").Should().Be("foo"); + TestMustHaveLengthWithDelegate("foo").Should().Be("foo"); + return; + + static string TestMustHaveLength(string? input) + { + input.MustHaveLength(3); + return input; + } + + static string TestMustHaveLengthWithDelegate(string? input) + { + input.MustHaveLength(3, (_, _) => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeLongerThan() + { + TestMustBeLongerThan("foobar").Should().Be("foobar"); + TestMustBeLongerThanWithDelegate("foobar").Should().Be("foobar"); + return; + + static string TestMustBeLongerThan(string? input) + { + input.MustBeLongerThan(3); + return input; + } + + static string TestMustBeLongerThanWithDelegate(string? input) + { + input.MustBeLongerThan(3, (_, _) => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeLongerThanOrEqualTo() + { + TestMustBeLongerThanOrEqualTo("foobar").Should().Be("foobar"); + TestMustBeLongerThanOrEqualToWithDelegate("foobar").Should().Be("foobar"); + return; + + static string TestMustBeLongerThanOrEqualTo(string? input) + { + input.MustBeLongerThanOrEqualTo(6); + return input; + } + + static string TestMustBeLongerThanOrEqualToWithDelegate(string? input) + { + input.MustBeLongerThanOrEqualTo(6, (_, _) => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustHaveLengthIn() + { + TestMustHaveLengthIn("foo").Should().Be("foo"); + TestMustHaveLengthInWithDelegate("foo").Should().Be("foo"); + return; + + static string TestMustHaveLengthIn(string? input) + { + input.MustHaveLengthIn(Range.FromInclusive(2).ToInclusive(4)); + return input; + } + + static string TestMustHaveLengthInWithDelegate(string? input) + { + input.MustHaveLengthIn(Range.FromInclusive(2).ToInclusive(4), (_, _) => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeNewLine() + { + TestMustBeNewLine("\n").Should().Be("\n"); + TestMustBeNewLineWithDelegate("\n").Should().Be("\n"); + return; + + static string TestMustBeNewLine(string? input) + { + input.MustBeNewLine(); + return input; + } + + static string TestMustBeNewLineWithDelegate(string? input) + { + input.MustBeNewLine(_ => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeTrimmed() + { + TestMustBeTrimmed("foo").Should().Be("foo"); + TestMustBeTrimmedWithDelegate("foo").Should().Be("foo"); + return; + + static string TestMustBeTrimmed(string? input) + { + input.MustBeTrimmed(); + return input; + } + + static string TestMustBeTrimmedWithDelegate(string? input) + { + input.MustBeTrimmed(_ => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeTrimmedAtStart() + { + TestMustBeTrimmedAtStart("foo").Should().Be("foo"); + TestMustBeTrimmedAtStartWithDelegate("foo").Should().Be("foo"); + return; + + static string TestMustBeTrimmedAtStart(string? input) + { + input.MustBeTrimmedAtStart(); + return input; + } + + static string TestMustBeTrimmedAtStartWithDelegate(string? input) + { + input.MustBeTrimmedAtStart(_ => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeTrimmedAtEnd() + { + TestMustBeTrimmedAtEnd("foo").Should().Be("foo"); + TestMustBeTrimmedAtEndWithDelegate("foo").Should().Be("foo"); + return; + + static string TestMustBeTrimmedAtEnd(string? input) + { + input.MustBeTrimmedAtEnd(); + return input; + } + + static string TestMustBeTrimmedAtEndWithDelegate(string? input) + { + input.MustBeTrimmedAtEnd(_ => new Exception()); + return input; + } + } + + [Fact] + public static void CheckImplements() + { + TestImplements(typeof(FileInfo), typeof(ISerializable)).Should().Be((typeof(FileInfo), typeof(ISerializable))); + TestImplementsWithComparer(typeof(FileInfo), typeof(ISerializable)).Should().Be((typeof(FileInfo), typeof(ISerializable), EqualityComparer.Default)); + return; + + static (Type Type, Type InterfaceType) TestImplements(Type? type, Type? interfaceType) + { + type!.Implements(interfaceType!); + return (type, interfaceType); + } + + static (Type Type, Type InterfaceType, IEqualityComparer Comparer) TestImplementsWithComparer(Type? type, Type? interfaceType) + { + type!.Implements(interfaceType!, EqualityComparer.Default); + return (type, interfaceType, EqualityComparer.Default); + } + } + + [Fact] + public static void CheckIsOrImplements() + { + TestIsOrImplements(typeof(FileInfo), typeof(ISerializable)).Should().Be((typeof(FileInfo), typeof(ISerializable))); + TestIsOrImplementsWithComparer(typeof(FileInfo), typeof(ISerializable)).Should().Be((typeof(FileInfo), typeof(ISerializable), EqualityComparer.Default)); + return; + + static (Type Type, Type InterfaceType) TestIsOrImplements(Type? type, Type? interfaceType) + { + type!.IsOrImplements(interfaceType!); + return (type, interfaceType); + } + + static (Type Type, Type InterfaceType, IEqualityComparer Comparer) TestIsOrImplementsWithComparer(Type? type, Type? interfaceType) + { + type!.IsOrImplements(interfaceType!, EqualityComparer.Default); + return (type, interfaceType, EqualityComparer.Default); + } + } + + [Fact] + public static void CheckDerivesFrom() + { + TestDerivesFrom(typeof(FileStream), typeof(Stream)).Should().Be((typeof(FileStream), typeof(Stream))); + TestDerivesFromWithTypeComparer(typeof(FileStream), typeof(Stream)).Should().Be((typeof(FileStream), typeof(Stream))); + return; + + static (Type Type, Type BaseType) TestDerivesFrom(Type? type, Type? baseType) + { + type!.DerivesFrom(baseType!); + return (type, baseType); + } + + static (Type Type, Type BaseType) TestDerivesFromWithTypeComparer(Type? type, Type? baseType) + { + type!.DerivesFrom(baseType!, EqualityComparer.Default); + return (type, baseType); + } + } + + [Fact] + public static void CheckIsOrDerivesFrom() + { + TestIsOrDerivesFrom(typeof(FileStream), typeof(Stream)).Should().Be((typeof(FileStream), typeof(Stream))); + TestIsOrDerivesFromWithComparer(typeof(FileStream), typeof(Stream)).Should().Be((typeof(FileStream), typeof(Stream), EqualityComparer.Default)); + return; + + static (Type Type, Type BaseType) TestIsOrDerivesFrom(Type? type, Type? baseType) + { + type!.IsOrDerivesFrom(baseType!); + return (type, baseType); + } + + static (Type Type, Type BaseType, IEqualityComparer Comparer) TestIsOrDerivesFromWithComparer(Type? type, Type? baseType) + { + type!.IsOrDerivesFrom(baseType!, EqualityComparer.Default); + return (type, baseType, EqualityComparer.Default); + } + } + + [Fact] + public static void CheckInheritsFrom() + { + TestInheritsFrom(typeof(FileStream), typeof(Stream)).Should().Be((typeof(FileStream), typeof(Stream))); + TestInheritsFromWithTypeComparer(typeof(FileStream), typeof(Stream)).Should().Be((typeof(FileStream), typeof(Stream), EqualityComparer.Default)); + return; + + static (Type Type, Type BaseType) TestInheritsFrom(Type? type, Type? baseType) + { + type!.InheritsFrom(baseType!); + return (type, baseType); + } + + static (Type Type, Type BaseType, IEqualityComparer Comparer) TestInheritsFromWithTypeComparer(Type? type, Type? baseType) + { + type!.InheritsFrom(baseType!, EqualityComparer.Default); + return (type, baseType, EqualityComparer.Default); + } + } + + + [Fact] + public static void CheckIsOrInheritsFrom() + { + TestIsOrInheritsFrom(typeof(FileStream), typeof(Stream)).Should().Be((typeof(FileStream), typeof(Stream))); + TestIsOrInheritsFromWithComparer(typeof(FileStream), typeof(Stream)).Should().Be((typeof(FileStream), typeof(Stream), EqualityComparer.Default)); + return; + + static (Type Type, Type BaseType) TestIsOrInheritsFrom(Type? type, Type? baseType) + { + type!.IsOrInheritsFrom(baseType!); + return (type, baseType); + } + + static (Type Type, Type BaseType, IEqualityComparer Comparer) TestIsOrInheritsFromWithComparer(Type? type, Type? baseType) + { + type!.IsOrInheritsFrom(baseType!, EqualityComparer.Default); + return (type, baseType, EqualityComparer.Default); + } + } + + [Fact] + public static void CheckMustBeAbsoluteUri() + { + TestMustBeAbsoluteUri(new Uri("https://example.com")).Should().Be(new Uri("https://example.com")); + TestMustBeAbsoluteUriWithDelegate(new Uri("https://example.com")).Should().Be(new Uri("https://example.com")); + return; + + static Uri TestMustBeAbsoluteUri(Uri? input) + { + input.MustBeAbsoluteUri(); + return input; + } + + static Uri TestMustBeAbsoluteUriWithDelegate(Uri? input) + { + input.MustBeAbsoluteUri(_ => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeRelativeUri() + { + TestMustBeRelativeUri(new Uri("/relative/path", UriKind.Relative)).Should().Be(new Uri("/relative/path", UriKind.Relative)); + TestMustBeRelativeUriWithDelegate(new Uri("/relative/path", UriKind.Relative)).Should().Be(new Uri("/relative/path", UriKind.Relative)); + return; + + static Uri TestMustBeRelativeUri(Uri? input) + { + input.MustBeRelativeUri(); + return input; + } + + static Uri TestMustBeRelativeUriWithDelegate(Uri? input) + { + input.MustBeRelativeUri(_ => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustHaveScheme() + { + TestMustHaveScheme(new Uri("https://example.com"), "https").Should().Be(new Uri("https://example.com")); + TestMustHaveSchemeWithDelegate(new Uri("https://example.com"), "https").Should().Be(new Uri("https://example.com")); + TestMustHaveSchemeWithSecondDelegate(new Uri("https://example.com"), "https").Should().Be(new Uri("https://example.com")); + return; + + static Uri TestMustHaveScheme(Uri? input, string scheme) + { + input.MustHaveScheme(scheme); + return input; + } + + static Uri TestMustHaveSchemeWithDelegate(Uri? input, string scheme) + { + input.MustHaveScheme(scheme, _ => new Exception()); + return input; + } + + static Uri TestMustHaveSchemeWithSecondDelegate(Uri? input, string scheme) + { + input.MustHaveScheme(scheme, (_, _) => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeHttpsUrl() + { + TestMustBeHttpsUrl(new Uri("https://example.com")).Should().Be(new Uri("https://example.com")); + TestMustBeHttpsUrlWithDelegate(new Uri("https://example.com")).Should().Be(new Uri("https://example.com")); + return; + + static Uri TestMustBeHttpsUrl(Uri? input) + { + input.MustBeHttpsUrl(); + return input; + } + + static Uri TestMustBeHttpsUrlWithDelegate(Uri? input) + { + input.MustBeHttpsUrl(_ => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeHttpUrl() + { + TestMustBeHttpUrl(new Uri("http://example.com")).Should().Be(new Uri("http://example.com")); + TestMustBeHttpUrlWithDelegate(new Uri("http://example.com")).Should().Be(new Uri("http://example.com")); + return; + + static Uri TestMustBeHttpUrl(Uri? input) + { + input.MustBeHttpUrl(); + return input; + } + + static Uri TestMustBeHttpUrlWithDelegate(Uri? input) + { + input.MustBeHttpUrl(_ => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustBeHttpOrHttpsUrl() + { + TestMustBeHttpOrHttpsUrl(new Uri("https://example.com")).Should().Be(new Uri("https://example.com")); + TestMustBeHttpOrHttpsUrlWithDelegate(new Uri("https://example.com")).Should().Be(new Uri("https://example.com")); + return; + + static Uri TestMustBeHttpOrHttpsUrl(Uri? input) + { + input.MustBeHttpOrHttpsUrl(); + return input; + } + + static Uri TestMustBeHttpOrHttpsUrlWithDelegate(Uri? input) + { + input.MustBeHttpOrHttpsUrl(_ => new Exception()); + return input; + } + } + + [Fact] + public static void CheckMustHaveOneSchemeOf() + { + TestMustHaveOneSchemeOf(new Uri("https://example.com")).Should().Be(new Uri("https://example.com")); + TestMustHaveOneSchemeOfWithDelegate(new Uri("https://example.com")).Should().Be(new Uri("https://example.com")); + return; + + static Uri TestMustHaveOneSchemeOf(Uri? input) + { + input.MustHaveOneSchemeOf(["http", "https"]); + return input; + } + + static Uri TestMustHaveOneSchemeOfWithDelegate(Uri? input) + { + input.MustHaveOneSchemeOf(new[] { "http", "https" }, (_, _) => new Exception()); + return input; + } + } + + [Fact] + public static void CheckIsIn() + { + TestIsIn("k").Should().Be("k"); + return; + + static string TestIsIn(string? input) + { + input!.IsIn(Range.FromInclusive("a").ToInclusive("z")); + return input; + } + } + + [Fact] + public static void CheckIsNotIn() + { + TestIsNotIn("k").Should().Be("k"); + return; + + static string TestIsNotIn(string? input) + { + input!.IsNotIn(Range.FromInclusive("a").ToInclusive("h")); + return input; + } + } + + [Fact] + public static void CheckIsSubstringOf() + { + TestIsSubstringOf("foo", "oo").Should().Be("foo"); + TestIsSubstringOfWithComparison("foo", "oo").Should().Be("foo"); + return; + + static string TestIsSubstringOf(string? input, string? comparison) + { + comparison!.IsSubstringOf(input!); + return input; + } + + static string TestIsSubstringOfWithComparison(string? input, string? comparison) + { + comparison!.IsSubstringOf(input!, StringComparison.OrdinalIgnoreCase); + return input; + } + } + + [Fact] + public static void CheckIsOpenConstructedGenericType() + { + TestIsOpenConstructedGenericType(typeof(List<>)).Should().Be(typeof(List<>)); + return; + + static Type TestIsOpenConstructedGenericType(Type? type) + { + type!.IsOpenConstructedGenericType(); + return type; + } + } + + [Fact] + public static void CheckAsList() + { + TestAsList(new List()).Should().Be(0); + TestAsListWithDelegate(new List()).Should().Be(0); + return; + + static int TestAsList(IList? list) + { + list!.AsList(); + return list.Count; + } + + static int TestAsListWithDelegate(IList? list) + { + list!.AsList(collection => new List(collection)); + return list.Count; + } + } + + [Fact] + public static void CheckAsArray() + { + TestAsArray([1, 2, 3]).Should().Be(3); + return; + + static int TestAsArray(int[]? array) + { + array!.AsArray(); + return array.Length; + } + } + + [Fact] + public static void CheckForEach() + { + TestForEach(["foo", "bar"]).Should().Be(2); + return; + + static int TestForEach(string[]? array) + { + array!.ForEach(_ => { }); + return array.Length; + } + } + + [Fact] + public static void CheckAsReadOnlyList() + { + TestAsReadOnlyList([1, 2, 3]).Should().Be(3); + TestAsReadOnlyListWithDelegate([1, 2, 3], collection => new List(collection)).Should().Be(3); + return; + + static int TestAsReadOnlyList(int[]? array) + { + array!.AsReadOnlyList(); + return array.Length; + } + + static int TestAsReadOnlyListWithDelegate(int[]? array, Func, IReadOnlyList>? collectionFactory) + { + array!.AsReadOnlyList(collectionFactory!); + return array.Length; + } + } + + [Fact] + public static void CheckCount() + { + TestCount(new ArrayList(new[] { 1, 2, 3 })).Should().Be(3); + TestCountWithParameterNameAndMessage(new ArrayList(new[] { 1, 2, 3 })).Should().Be(3); + return; + + static int TestCount(ICollection? collection) + { + collection!.Count(); + return collection.Count; + } + + static int TestCountWithParameterNameAndMessage(ICollection? collection) + { + collection!.Count(nameof(collection), "The collection must not be null"); + return collection.Count; + } + } + + [Fact] + public static void CheckGetCount() + { + TestGetCount([1, 2, 3]).Should().Be(3); + TestGetCountWithParameterNameAndMessage([1, 2, 3]).Should().Be(3); + return; + + static int TestGetCount(ICollection? collection) + { + collection!.GetCount(); + return collection.Count; + } + + static int TestGetCountWithParameterNameAndMessage(ICollection? collection) + { + collection!.GetCount(nameof(collection), "The collection must not be null"); + return collection.Count; + } + } + + [Fact] + public static void CheckExtractProperty() + { + TestExtractProperty((string a) => a.Length).Should().NotBeNull(); + return; + + static Type TestExtractProperty(Expression>? expression) + { + expression!.ExtractProperty(); + return expression.Type; + } + } + + [Fact] + public static void CheckExtractField() + { + TestExtractField((TestClassWithPublicField x) => x.PublicField).Should().NotBeNull(); + return; + + static Type TestExtractField(Expression>? expression) + { + expression!.ExtractField(); + return expression.Type; + } + } + + private sealed class TestClassWithPublicField + { +#pragma warning disable CS0414 // Field is assigned but its value is never used + // ReSharper disable once ConvertToConstant.Local + public readonly int PublicField = 42; +#pragma warning restore CS0414 + } + + [Fact] + public static void CheckAppendCollectionContent() + { + var stringBuilder = new StringBuilder(); + TestAppendCollectionContent(stringBuilder, [1, 2, 3]).Should().Be((stringBuilder, 3)); + return; + + static (StringBuilder, int) TestAppendCollectionContent(StringBuilder? stringBuilder, ICollection? items) + { + stringBuilder!.AppendCollectionContent(items!); + return (stringBuilder, items.Count); + } + } + + [Fact] + public static void CheckToStringRepresentation() + { + TestToStringRepresentation("foo").Should().Be(3); + return; + + static int TestToStringRepresentation(string? value) + { + value.ToStringRepresentation(); + return value.Length; + } + } + + [Fact] + public static void CheckAppendItemsWithNewLine() + { + var stringBuilder = new StringBuilder(); + TestAppendItemsWithNewLine(stringBuilder, [1, 2, 3]).Should().Be((stringBuilder, 3)); + return; + + static (StringBuilder, int) TestAppendItemsWithNewLine(StringBuilder? stringBuilder, ICollection? items) + { + stringBuilder!.AppendItemsWithNewLine(items!); + return (stringBuilder, items.Count); + } + } + + [Fact] + public static void CheckAppendIf() + { + var stringBuilder = new StringBuilder(); + TestAppendIf(stringBuilder, true, "foo").Should().Be((stringBuilder, 3)); + return; + + static (StringBuilder, int) TestAppendIf(StringBuilder? stringBuilder, bool condition, string value) + { + stringBuilder!.AppendIf(condition, value); + return (stringBuilder, value.Length); + } + } + + [Fact] + public static void CheckAppendLineIf() + { + var stringBuilder = new StringBuilder(); + TestAppendLineIf(stringBuilder, true, "foo").Should().Be((stringBuilder, 3)); + return; + + static (StringBuilder, int) TestAppendLineIf(StringBuilder? stringBuilder, bool condition, string value) + { + stringBuilder!.AppendLineIf(condition, value); + return (stringBuilder, value.Length); + } + } + + [Fact] + public static void CheckAppendItems() + { + var stringBuilder = new StringBuilder(); + TestAppendItems(stringBuilder, [1, 2, 3]).Should().Be((stringBuilder, 3)); + return; + + static (StringBuilder, int) TestAppendItems(StringBuilder? stringBuilder, ICollection? items) + { + stringBuilder!.AppendItems(items!); + return (stringBuilder, items.Count); + } + } + + [Fact] + public static void CheckAppendExceptionMessages() + { + var stringBuilder = new StringBuilder(); + TestAppendExceptionMessages(stringBuilder, new Exception("foo")).Should().Be((stringBuilder, 3)); + return; + + static (StringBuilder, int) TestAppendExceptionMessages(StringBuilder? stringBuilder, Exception exception) + { + stringBuilder!.AppendExceptionMessages(exception); + return (stringBuilder, exception.Message.Length); + } + } + + [Fact] + public static void CheckGetAllExceptionMessages() + { + TestGetAllExceptionMessages(new Exception("foo")).Should().Be(3); + return; + + static int TestGetAllExceptionMessages(Exception? exception) + { + exception!.GetAllExceptionMessages(); + return exception.Message.Length; + } + } +} \ No newline at end of file diff --git a/Code/Light.GuardClauses.Tests/Light.GuardClauses.Tests.csproj b/Code/Light.GuardClauses.Tests/Light.GuardClauses.Tests.csproj index 2633bd0..34f0e4e 100644 --- a/Code/Light.GuardClauses.Tests/Light.GuardClauses.Tests.csproj +++ b/Code/Light.GuardClauses.Tests/Light.GuardClauses.Tests.csproj @@ -1,19 +1,23 @@ - - - - - - net8.0 - false - 12 - - - - - - - - - - + + + + + + net8.0 + false + 12 + true + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + \ No newline at end of file diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeSubstringOfTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeSubstringOfTests.cs index 3408d69..d6079be 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeSubstringOfTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeSubstringOfTests.cs @@ -94,7 +94,6 @@ public static void CustomException(string first, string second) => [InlineData("Foo", "FOO", StringComparison.Ordinal)] [InlineData("Bar", null, StringComparison.OrdinalIgnoreCase)] [InlineData(null, "Baz", StringComparison.CurrentCulture)] - [InlineData("Qux", "Quux", (StringComparison) 509)] public static void CustomExceptionCustomComparisonType(string first, string second, StringComparison comparisonType) => Test.CustomException(first, second, diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeTests.cs index 81b2285..6466858 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeTests.cs @@ -50,7 +50,8 @@ public static void StringsNotEqualIgnoreWhiteSpace(string first, string second, public static void CustomException() => Test.CustomException("Foo", "Bar", - (x, y, exceptionFactory) => x.MustBe(y, StringComparison.Ordinal, exceptionFactory)); + StringComparison.Ordinal, + (x, y, ct, exceptionFactory) => x.MustBe(y, ct, exceptionFactory)); [Fact] public static void CustomMessage() => @@ -60,7 +61,8 @@ public static void CustomMessage() => public static void CustomExceptionIgnoreWhiteSpace() => Test.CustomException("Foo", " foo", - (x, y, exceptionFactory) => x.MustBe(y, StringComparisonType.OrdinalIgnoreWhiteSpace, exceptionFactory)); + StringComparisonType.OrdinalIgnoreWhiteSpace, + (x, y, ct, exceptionFactory) => x.MustBe(y, ct, exceptionFactory)); [Fact] public static void CustomMessageIgnoreWhiteSpace() => diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustContainTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustContainTests.cs index 70a594d..3912163 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustContainTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustContainTests.cs @@ -79,7 +79,6 @@ public static void CustomException(string first, string second) => [InlineData("Foo", "foo", StringComparison.Ordinal)] [InlineData(null, "Bar", StringComparison.OrdinalIgnoreCase)] [InlineData("Baz", null, StringComparison.CurrentCulture)] - [InlineData("Qux", "Qux", (StringComparison) 42)] public static void CustomExceptionCustomSearch(string x, string y, StringComparison comparison) => Test.CustomException(x, y, diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustEndWithTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustEndWithTests.cs new file mode 100644 index 0000000..776665d --- /dev/null +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustEndWithTests.cs @@ -0,0 +1,73 @@ +using System; +using FluentAssertions; +using Light.GuardClauses.Exceptions; +using Xunit; + +namespace Light.GuardClauses.Tests.StringAssertions; + +public static class MustEndWithTests +{ + [Theory] + [InlineData("Foo", "Bar")] + [InlineData("When you play the game of thrones you win, or you die.", "you live")] + public static void DoesNotEndWith(string x, string y) + { + var act = () => x.MustEndWith(y); + + var exception = act.Should().Throw().Which; + exception.Message.Should().StartWith($"{nameof(x)} must end with \"{y}\" (CurrentCulture), but it actually is \"{x}\"."); + exception.ParamName.Should().BeSameAs(nameof(x)); + } + + [Theory] + [InlineData("FooBar", "Bar", StringComparison.Ordinal)] + [InlineData("12345678", "5678", StringComparison.InvariantCultureIgnoreCase)] + public static void EndsWith(string x, string y, StringComparison comparisonType) => + x.MustEndWith(y, comparisonType).Should().BeSameAs(x); + + [Fact] + public static void ParameterNull() + { + var act = () => ((string) null).MustEndWith("Foo"); + + act.Should().Throw(); + } + + [Fact] + public static void ValueNull() + { + var act = () => "Foo".MustEndWith(null!); + + act.Should().Throw(); + } + + [Fact] + public static void CustomMessage() => + Test.CustomMessage(message => "Foo".MustEndWith("Bar", message: message)); + + [Fact] + public static void CustomMessageParameterNull() => + Test.CustomMessage(message => ((string) null).MustEndWith("Bar", message: message)); + + [Fact] + public static void EndsWithCustomException() => + "Foo".MustEndWith("o", (_, _) => new Exception()).Should().Be("Foo"); + + [Fact] + public static void EndsWithCustomExceptionAndComparisonType() => + "Foo".MustEndWith("O", StringComparison.OrdinalIgnoreCase, (_, _, _) => new Exception()).Should().Be("Foo"); + + [Theory] + [InlineData("Foo", "Bar")] + [InlineData("Foo", null)] + [InlineData(null, "Baz")] + public static void CustomException(string first, string second) => + Test.CustomException(first, second, (s1, s2, exceptionFactory) => s1.MustEndWith(s2, exceptionFactory)); + + [Theory] + [InlineData("Foo", "Bar", StringComparison.Ordinal)] + [InlineData(null, "Bar", StringComparison.Ordinal)] + [InlineData("Baz", null, StringComparison.Ordinal)] + public static void CustomExceptionWithComparisonType(string a, string b, StringComparison comparisonType) => + Test.CustomException(a, b, comparisonType, (s1, s2, ct, exceptionFactory) => s1.MustEndWith(s2, ct, exceptionFactory)); +} diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustNotBeSubstringOfTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotBeSubstringOfTests.cs index 8c49034..aac5a97 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustNotBeSubstringOfTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotBeSubstringOfTests.cs @@ -93,7 +93,6 @@ public static void CustomException(string first, string second) => [InlineData("Full of Possibilities", "Death is so terribly final, while life is full of possibilities", StringComparison.OrdinalIgnoreCase)] [InlineData(null, "Foo", StringComparison.Ordinal)] [InlineData("Bar", null, StringComparison.CurrentCulture)] - [InlineData("Baz", "Qux", (StringComparison) (-14))] public static void CustomExceptionCustomComparison(string a, string b, StringComparison comparisonType) => Test.CustomException(a, b, diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustNotBeTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotBeTests.cs index 6d6f28a..b412c7c 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustNotBeTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotBeTests.cs @@ -56,7 +56,8 @@ public static void CustomException() => public static void CustomExceptionIgnoreWhiteSpace() => Test.CustomException("Foo", " Foo", - (x, y, exceptionFactory) => x.MustNotBe(y, StringComparisonType.OrdinalIgnoreWhiteSpace, exceptionFactory)); + StringComparisonType.OrdinalIgnoreWhiteSpace, + (x, y, ct, exceptionFactory) => x.MustNotBe(y, ct, exceptionFactory)); [Fact] public static void CustomMessage() => diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustNotContainTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotContainTests.cs index e7511fc..923cc49 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustNotContainTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotContainTests.cs @@ -64,7 +64,6 @@ public static void CustomException(string first, string second) => [InlineData("Foo", "O", StringComparison.OrdinalIgnoreCase)] [InlineData("Bar", null, StringComparison.CurrentCulture)] [InlineData(null, "Baz", StringComparison.CurrentCultureIgnoreCase)] - [InlineData("Qux", "Quux", (StringComparison) 504)] public static void CustomExceptionCustomSearch(string first, string second, StringComparison comparisonType) => Test.CustomException(first, second, diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustNotEndWithTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotEndWithTests.cs new file mode 100644 index 0000000..d950826 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotEndWithTests.cs @@ -0,0 +1,73 @@ +using System; +using FluentAssertions; +using Light.GuardClauses.Exceptions; +using Xunit; + +namespace Light.GuardClauses.Tests.StringAssertions; + +public static class MustNotEndWithTests +{ + [Theory] + [InlineData("FooBar", "Bar")] + [InlineData("When you play the game of thrones you win, or you die.", "die.")] + public static void DoesEndWith(string x, string y) + { + var act = () => x.MustNotEndWith(y); + + var exception = act.Should().Throw().Which; + exception.Message.Should().StartWith($"{nameof(x)} must not end with \"{y}\" (CurrentCulture), but it actually is \"{x}\"."); + exception.ParamName.Should().BeSameAs(nameof(x)); + } + + [Theory] + [InlineData("Foo", "Bar", StringComparison.Ordinal)] + [InlineData("12345", "1234", StringComparison.InvariantCultureIgnoreCase)] + public static void DoesNotEndWith(string x, string y, StringComparison comparisonType) => + x.MustNotEndWith(y, comparisonType).Should().BeSameAs(x); + + [Fact] + public static void ParameterNull() + { + var act = () => ((string) null).MustNotEndWith("Foo"); + + act.Should().Throw(); + } + + [Fact] + public static void ValueNull() + { + var act = () => "Foo".MustNotEndWith(null!); + + act.Should().Throw(); + } + + [Fact] + public static void CustomMessage() => + Test.CustomMessage(message => "FooBar".MustNotEndWith("Bar", message: message)); + + [Fact] + public static void CustomMessageParameterNull() => + Test.CustomMessage(message => ((string) null).MustNotEndWith("Bar", message: message)); + + [Fact] + public static void DoesNotEndWithCustomException() => + "Foo".MustNotEndWith("r", (_, _) => new Exception()).Should().Be("Foo"); + + [Fact] + public static void DoesNotEndWithCustomExceptionAndComparisonType() => + "Foo".MustNotEndWith("R", StringComparison.OrdinalIgnoreCase, (_, _, _) => new Exception()).Should().Be("Foo"); + + [Theory] + [InlineData("FooBar", "Bar")] + [InlineData("Foo", null)] + [InlineData(null, "Baz")] + public static void CustomException(string first, string second) => + Test.CustomException(first, second, (s1, s2, exceptionFactory) => s1.MustNotEndWith(s2, exceptionFactory)); + + [Theory] + [InlineData("FooBar", "Bar", StringComparison.Ordinal)] + [InlineData(null, "Bar", StringComparison.Ordinal)] + [InlineData("Baz", null, StringComparison.Ordinal)] + public static void CustomExceptionWithComparisonType(string a, string b, StringComparison comparisonType) => + Test.CustomException(a, b, comparisonType, (s1, s2, ct, exceptionFactory) => s1.MustNotEndWith(s2, ct, exceptionFactory)); +} diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustNotStartWithTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotStartWithTests.cs new file mode 100644 index 0000000..5ad5160 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustNotStartWithTests.cs @@ -0,0 +1,85 @@ +using System; +using FluentAssertions; +using Light.GuardClauses.Exceptions; +using Xunit; + +namespace Light.GuardClauses.Tests.StringAssertions; + +public static class MustNotStartWithTests +{ + [Theory] + [InlineData("Bar", "Foo")] + [InlineData("When you play the game of thrones you win, or you die.", "Love and peace")] + public static void DoesNotStartWith(string x, string y) => + x.MustNotStartWith(y).Should().BeSameAs(x); + + [Theory] + [InlineData("FooBar", "Foo")] + [InlineData("12345678", "1234")] + public static void StartsWith(string x, string y) + { + var act = () => x.MustNotStartWith(y); + + var exception = act.Should().Throw().Which; + exception.Message.Should().StartWith($"{nameof(x)} must not start with \"{y}\" (CurrentCulture), but it actually is \"{x}\""); + exception.ParamName.Should().BeSameAs(nameof(x)); + } + + [Theory] + [InlineData("FooBar", "Foo", StringComparison.Ordinal)] + [InlineData("12345678", "1234", StringComparison.InvariantCultureIgnoreCase)] + public static void StartsWithComparisonType(string x, string y, StringComparison comparisonType) + { + var act = () => x.MustNotStartWith(y, comparisonType); + + var exception = act.Should().Throw().Which; + exception.Message.Should().StartWith($"{nameof(x)} must not start with \"{y}\" ({comparisonType}), but it actually is \"{x}\""); + exception.ParamName.Should().BeSameAs(nameof(x)); + } + + [Fact] + public static void ParameterNull() + { + var act = () => ((string)null).MustNotStartWith("Foo"); + + act.Should().Throw(); + } + + [Fact] + public static void ValueNull() + { + var act = () => "Foo".MustNotStartWith(null!); + + act.Should().Throw(); + } + + [Fact] + public static void CustomMessage() => + Test.CustomMessage(message => "FooBar".MustNotStartWith("Foo", message: message)); + + [Fact] + public static void CustomMessageParameterNull() => + Test.CustomMessage(message => ((string)null).MustNotStartWith("Bar", message: message)); + + [Fact] + public static void NotStartsWithCustomException() => + "Bar".MustNotStartWith("Foo", (_, _) => new Exception()).Should().Be("Bar"); + + [Fact] + public static void NotStartsWithCustomExceptionAndComparisonType() => + "Bar".MustNotStartWith("foo", StringComparison.OrdinalIgnoreCase, (_, _, _) => new Exception()).Should().Be("Bar"); + + [Theory] + [InlineData("Foo", "Foo")] + [InlineData("Foo", null)] + [InlineData(null, "Baz")] + public static void CustomException(string first, string second) => + Test.CustomException(first, second, (s1, s2, exceptionFactory) => s1.MustNotStartWith(s2, exceptionFactory)); + + [Theory] + [InlineData("Foo", "foo", StringComparison.OrdinalIgnoreCase)] + [InlineData(null, "Bar", StringComparison.Ordinal)] + [InlineData("Baz", null, StringComparison.Ordinal)] + public static void CustomExceptionWithComparisonType(string a, string b, StringComparison comparisonType) => + Test.CustomException(a, b, comparisonType, (s1, s2, ct, exceptionFactory) => s1.MustNotStartWith(s2, ct, exceptionFactory)); +} diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustStartWithTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustStartWithTests.cs new file mode 100644 index 0000000..34d1237 --- /dev/null +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustStartWithTests.cs @@ -0,0 +1,73 @@ +using System; +using FluentAssertions; +using Light.GuardClauses.Exceptions; +using Xunit; + +namespace Light.GuardClauses.Tests.StringAssertions; + +public static class MustStartWithTests +{ + [Theory] + [InlineData("Foo", "Bar")] + [InlineData("When you play the game of thrones you win, or you die. There is no middle ground.", "Love and peace")] + public static void DoesNotStartWith(string x, string y) + { + var act = () => x.MustStartWith(y); + + var exception = act.Should().Throw().Which; + exception.Message.Should().StartWith($"{nameof(x)} must start with \"{y}\" (CurrentCulture), but it actually is \"{x}\"."); + exception.ParamName.Should().BeSameAs(nameof(x)); + } + + [Theory] + [InlineData("FooBar", "Foo", StringComparison.Ordinal)] + [InlineData("12345678", "1234", StringComparison.InvariantCultureIgnoreCase)] + public static void StartsWith(string x, string y, StringComparison comparisonType) => + x.MustStartWith(y, comparisonType).Should().BeSameAs(x); + + [Fact] + public static void ParameterNull() + { + var act = () => ((string) null).MustStartWith("Foo"); + + act.Should().Throw(); + } + + [Fact] + public static void ValueNull() + { + var act = () => "Foo".MustStartWith(null!); + + act.Should().Throw(); + } + + [Fact] + public static void CustomMessage() => + Test.CustomMessage(message => "Foo".MustStartWith("Bar", message: message)); + + [Fact] + public static void CustomMessageParameterNull() => + Test.CustomMessage(message => ((string) null).MustStartWith("Bar", message: message)); + + [Fact] + public static void StartsWithCustomException() => + "Foo".MustStartWith("F", (_, _) => new Exception()).Should().Be("Foo"); + + [Fact] + public static void StartsWithCustomExceptionAndComparisonType() => + "Foo".MustStartWith("f", StringComparison.OrdinalIgnoreCase, (_, _, _) => new Exception()).Should().Be("Foo"); + + [Theory] + [InlineData("Foo", "Bar")] + [InlineData("Foo", null)] + [InlineData(null, "Baz")] + public static void CustomException(string first, string second) => + Test.CustomException(first, second, (s1, s2, exceptionFactory) => s1.MustStartWith(s2, exceptionFactory)); + + [Theory] + [InlineData("Foo", "Bar", StringComparison.Ordinal)] + [InlineData(null, "Bar", StringComparison.Ordinal)] + [InlineData("Baz", null, StringComparison.Ordinal)] + public static void CustomExceptionWithComparisonType(string a, string b, StringComparison comparisonType) => + Test.CustomException(a, b, comparisonType, (s1, s2, ct, exceptionFactory) => s1.MustStartWith(s2, ct, exceptionFactory)); +} \ No newline at end of file diff --git a/Code/Light.GuardClauses.sln.DotSettings b/Code/Light.GuardClauses.sln.DotSettings index 14c11e8..47d8c24 100644 --- a/Code/Light.GuardClauses.sln.DotSettings +++ b/Code/Light.GuardClauses.sln.DotSettings @@ -249,6 +249,7 @@ True True True + True True True True diff --git a/Code/Light.GuardClauses/Check.CommonAssertions.cs b/Code/Light.GuardClauses/Check.CommonAssertions.cs index 73a6379..0104f58 100644 --- a/Code/Light.GuardClauses/Check.CommonAssertions.cs +++ b/Code/Light.GuardClauses/Check.CommonAssertions.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using JetBrains.Annotations; using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; @@ -20,7 +21,7 @@ public static partial class Check /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNull([ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class + public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : class { if (parameter is null) Throw.ArgumentNull(parameterName, message); @@ -35,7 +36,7 @@ public static T MustNotBeNull([ValidatedNotNull, NoEnumeration] this T? param /// Your custom exception thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNull([ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) where T : class + public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) where T : class { if (parameter is null) Throw.CustomException(exceptionFactory); @@ -53,7 +54,7 @@ public static T MustNotBeNull([ValidatedNotNull, NoEnumeration] this T? param /// Thrown when is a value type and the default value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeDefault([ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (default(T) is null) { @@ -64,7 +65,11 @@ public static T MustNotBeDefault([ValidatedNotNull] this T parameter, [Caller if (EqualityComparer.Default.Equals(parameter, default!)) Throw.ArgumentDefault(parameterName, message); + + // If we end up here, we have a value type which cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. return parameter; +#pragma warning restore CS8777 } /// @@ -75,7 +80,7 @@ public static T MustNotBeDefault([ValidatedNotNull] this T parameter, [Caller /// Your custom exception thrown when is the default value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeDefault([ValidatedNotNull] this T parameter, Func exceptionFactory) + public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, Func exceptionFactory) { if (default(T) is null) { @@ -86,7 +91,11 @@ public static T MustNotBeDefault([ValidatedNotNull] this T parameter, Func.Default.Equals(parameter, default!)) Throw.CustomException(exceptionFactory); + + // If we end up here, we have a value type which cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. return parameter; +#pragma warning restore CS8777 } @@ -101,10 +110,15 @@ public static T MustNotBeDefault([ValidatedNotNull] this T parameter, FuncThrown when is a reference type and is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNullReference([ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (default(T) != null) + { + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. return parameter; +#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + } if (parameter is null) Throw.ArgumentNull(parameterName, message); @@ -121,10 +135,15 @@ public static T MustNotBeNullReference([ValidatedNotNull, NoEnumeration] this /// Your custom exception thrown when is a reference type and is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNullReference([ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) + public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) { if (default(T) != null) + { + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. return parameter; +#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + } if (parameter is null) Throw.CustomException(exceptionFactory); @@ -141,7 +160,7 @@ public static T MustNotBeNullReference([ValidatedNotNull, NoEnumeration] this /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeOfType([ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message) is T castValue) return castValue; @@ -158,7 +177,7 @@ public static T MustBeOfType([ValidatedNotNull, NoEnumeration] this object? p /// Your custom exception thrown when cannot be cast to . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeOfType([ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) { if (parameter is T castValue) return castValue; @@ -328,7 +347,7 @@ public static void InvalidArgument(bool condition, T parameter, FuncThe message that will be passed to the resulting exception (optional). /// Thrown when has no value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustHaveValue([NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : struct + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : struct { if (!parameter.HasValue) Throw.NullableHasNoValue(parameterName, message); @@ -344,7 +363,7 @@ public static T MustHaveValue([NoEnumeration] this T? parameter, [CallerArgum /// Thrown when has no value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] - public static T MustHaveValue([NoEnumeration] this T? parameter, Func exceptionFactory) where T : struct + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) where T : struct { if (!parameter.HasValue) Throw.CustomException(exceptionFactory); diff --git a/Code/Light.GuardClauses/Check.ComparableAssertions.cs b/Code/Light.GuardClauses/Check.ComparableAssertions.cs index d825198..0eb9020 100644 --- a/Code/Light.GuardClauses/Check.ComparableAssertions.cs +++ b/Code/Light.GuardClauses/Check.ComparableAssertions.cs @@ -2,6 +2,7 @@ using JetBrains.Annotations; using System.Runtime.CompilerServices; using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; @@ -24,7 +25,7 @@ public static partial class Check /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThan([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustNotBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) Throw.MustNotBeLessThan(parameter, other, parameterName, message); @@ -40,9 +41,9 @@ public static T MustNotBeLessThan([ValidatedNotNull] this T parameter, T othe /// Your custom exception thrown when the specified is less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThan([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable + public static T MustNotBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs off if (parameter is null || parameter.CompareTo(other) < 0) Throw.CustomException(exceptionFactory, parameter!, other); return parameter; @@ -59,7 +60,7 @@ public static T MustNotBeLessThan([ValidatedNotNull] this T parameter, T othe /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThanOrEqualTo([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); @@ -75,9 +76,9 @@ public static T MustBeGreaterThanOrEqualTo([ValidatedNotNull] this T paramete /// Your custom exception thrown when the specified is less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThanOrEqualTo([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable + public static T MustBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || parameter.CompareTo(other) < 0) Throw.CustomException(exceptionFactory, parameter!, other); return parameter; @@ -100,7 +101,7 @@ public static T MustBeGreaterThanOrEqualTo([ValidatedNotNull] this T paramete /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThan([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) Throw.MustBeLessThan(parameter, other, parameterName, message); @@ -116,9 +117,9 @@ public static T MustBeLessThan([ValidatedNotNull] this T parameter, T other, /// Your custom exception thrown when the specified is not less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThan([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable + public static T MustBeLessThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || parameter.CompareTo(other) >= 0) Throw.CustomException(exceptionFactory, parameter!, other); return parameter; @@ -135,7 +136,7 @@ public static T MustBeLessThan([ValidatedNotNull] this T parameter, T other, /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThanOrEqualTo([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustNotBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) Throw.MustNotBeGreaterThanOrEqualTo(parameter, other, parameterName, message); @@ -151,9 +152,9 @@ public static T MustNotBeGreaterThanOrEqualTo([ValidatedNotNull] this T param /// Your custom exception thrown when the specified is not less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThanOrEqualTo([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable + public static T MustNotBeGreaterThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || parameter.CompareTo(other) >= 0) Throw.CustomException(exceptionFactory, parameter!, other); return parameter; @@ -176,7 +177,7 @@ public static T MustNotBeGreaterThanOrEqualTo([ValidatedNotNull] this T param /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThan([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) Throw.MustBeGreaterThan(parameter, other, parameterName, message); @@ -192,9 +193,9 @@ public static T MustBeGreaterThan([ValidatedNotNull] this T parameter, T othe /// Your custom exception thrown when the specified is less than or equal to , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThan([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable + public static T MustBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || parameter.CompareTo(other) <= 0) Throw.CustomException(exceptionFactory, parameter!, other); return parameter; @@ -211,7 +212,7 @@ public static T MustBeGreaterThan([ValidatedNotNull] this T parameter, T othe /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustNotBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); @@ -227,9 +228,9 @@ public static T MustNotBeLessThanOrEqualTo([ValidatedNotNull] this T paramete /// Your custom exception thrown when the specified is less than or equal to , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable + public static T MustNotBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || parameter.CompareTo(other) <= 0) Throw.CustomException(exceptionFactory, parameter!, other); return parameter; @@ -252,7 +253,7 @@ public static T MustNotBeLessThanOrEqualTo([ValidatedNotNull] this T paramete /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThan([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); @@ -268,9 +269,9 @@ public static T MustNotBeGreaterThan([ValidatedNotNull] this T parameter, T o /// Your custom exception thrown when the specified is greater than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThan([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || parameter.CompareTo(other) > 0) Throw.CustomException(exceptionFactory, parameter!, other); return parameter; @@ -287,7 +288,7 @@ public static T MustNotBeGreaterThan([ValidatedNotNull] this T parameter, T o /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) Throw.MustBeLessThanOrEqualTo(parameter, other, parameterName, message); @@ -303,9 +304,9 @@ public static T MustBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, /// Your custom exception thrown when the specified is greater than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable + public static T MustBeLessThanOrEqualTo([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || parameter.CompareTo(other) > 0) Throw.CustomException(exceptionFactory, parameter!, other); return parameter; @@ -324,7 +325,7 @@ public static T MustBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, /// True if the parameter is within the specified range, else false. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsIn([ValidatedNotNull] this T parameter, Range range) where T : IComparable => range.IsValueWithinRange(parameter); + public static bool IsIn([NotNull, ValidatedNotNull] this T parameter, Range range) where T : IComparable => range.IsValueWithinRange(parameter); /// /// Checks if the value is not within the specified range. @@ -334,7 +335,7 @@ public static T MustBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, /// True if the parameter is not within the specified range, else false. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsNotIn([ValidatedNotNull] this T parameter, Range range) where T : IComparable => !range.IsValueWithinRange(parameter); + public static bool IsNotIn([NotNull, ValidatedNotNull] this T parameter, Range range) where T : IComparable => !range.IsValueWithinRange(parameter); /// /// Ensures that is within the specified range, or otherwise throws an . @@ -348,7 +349,7 @@ public static T MustBeLessThanOrEqualTo([ValidatedNotNull] this T parameter, /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeIn([ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) Throw.MustBeInRange(parameter, range, parameterName, message); @@ -364,9 +365,9 @@ public static T MustBeIn([ValidatedNotNull] this T parameter, Range range, /// Your custom exception thrown when is not within , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeIn([ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) where T : IComparable + public static T MustBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || !range.IsValueWithinRange(parameter)) Throw.CustomException(exceptionFactory, parameter!, range); return parameter; @@ -384,7 +385,7 @@ public static T MustBeIn([ValidatedNotNull] this T parameter, Range range, /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeIn([ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable + public static T MustNotBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) Throw.MustNotBeInRange(parameter, range, parameterName, message); @@ -400,9 +401,9 @@ public static T MustNotBeIn([ValidatedNotNull] this T parameter, Range ran /// Your custom exception thrown when is within , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeIn([ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) where T : IComparable + public static T MustNotBeIn([NotNull, ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || range.IsValueWithinRange(parameter)) Throw.CustomException(exceptionFactory, parameter!, range); return parameter; diff --git a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs index 3392408..a67209b 100644 --- a/Code/Light.GuardClauses/Check.EnumerableAssertions.cs +++ b/Code/Light.GuardClauses/Check.EnumerableAssertions.cs @@ -7,6 +7,7 @@ using JetBrains.Annotations; using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; @@ -23,11 +24,11 @@ public static partial class Check /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable + public static TCollection MustHaveCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable { if (parameter!.Count(parameterName, message) != count) - Throw.InvalidCollectionCount(parameter!, count, parameterName, message); - return parameter!; + Throw.InvalidCollectionCount(parameter, count, parameterName, message); + return parameter; } /// @@ -39,7 +40,7 @@ public static TCollection MustHaveCount([ValidatedNotNull] this TCo /// Your custom exception thrown when does not have the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) where TCollection : class, IEnumerable + public static TCollection MustHaveCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) where TCollection : class, IEnumerable { if (parameter is null || parameter.Count() != count) Throw.CustomException(exceptionFactory, parameter, count); @@ -66,11 +67,11 @@ public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collecti /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([ValidatedNotNull] this TCollection? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable + public static TCollection MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this TCollection? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable { if (parameter.Count(parameterName, message) == 0) Throw.EmptyCollection(parameterName, message); - return parameter!; + return parameter; } /// @@ -81,7 +82,7 @@ public static TCollection MustNotBeNullOrEmpty([ValidatedNotNull] t /// Thrown when has no items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([ValidatedNotNull] this TCollection? parameter, Func exceptionFactory) where TCollection : class, IEnumerable + public static TCollection MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this TCollection? parameter, Func exceptionFactory) where TCollection : class, IEnumerable { if (parameter is null || parameter.Count() == 0) Throw.CustomException(exceptionFactory, parameter); @@ -99,7 +100,7 @@ public static TCollection MustNotBeNullOrEmpty([ValidatedNotNull] t /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable + public static TCollection MustContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable { if (parameter is ICollection collection) { @@ -109,8 +110,8 @@ public static TCollection MustContain([ValidatedNotNull] thi } if (!parameter.MustNotBeNull(parameterName, message).Contains(item)) - Throw.MissingItem(parameter!, item, parameterName, message); - return parameter!; + Throw.MissingItem(parameter, item, parameterName, message); + return parameter; } /// @@ -122,7 +123,7 @@ public static TCollection MustContain([ValidatedNotNull] thi /// Your custom exception thrown when does not contain , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) where TCollection : class, IEnumerable + public static TCollection MustContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) where TCollection : class, IEnumerable { if (parameter is ICollection collection) { @@ -147,7 +148,7 @@ public static TCollection MustContain([ValidatedNotNull] thi /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable + public static TCollection MustNotContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable { if (parameter is ICollection collection) { @@ -157,8 +158,8 @@ public static TCollection MustNotContain([ValidatedNotNull] } if (parameter.MustNotBeNull(parameterName, message).Contains(item)) - Throw.ExistingItem(parameter!, item, parameterName, message); - return parameter!; + Throw.ExistingItem(parameter, item, parameterName, message); + return parameter; } /// @@ -170,7 +171,7 @@ public static TCollection MustNotContain([ValidatedNotNull] /// Your custom exception thrown when contains . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) where TCollection : class, IEnumerable + public static TCollection MustNotContain([NotNull, ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) where TCollection : class, IEnumerable { if (parameter is ICollection collection) { @@ -192,7 +193,8 @@ public static TCollection MustNotContain([ValidatedNotNull] /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("items:null => halt")] - public static bool IsOneOf(this TItem item, [ValidatedNotNull] IEnumerable items) + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static bool IsOneOf(this TItem item, [NotNull, ValidatedNotNull] IEnumerable items) { if (items is ICollection collection) return collection.Contains(item); @@ -214,7 +216,8 @@ public static bool IsOneOf(this TItem item, [ValidatedNotNull] IEnumerabl /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("items:null => halt")] - public static TItem MustBeOneOf(this TItem parameter, [ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static TItem MustBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { // ReSharper disable PossibleMultipleEnumeration if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) @@ -232,9 +235,9 @@ public static TItem MustBeOneOf(this TItem parameter, [ValidatedNotNull] /// Your custom exception thrown when is not equal to one of the specified , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("items:null => halt")] - public static TItem MustBeOneOf(this TItem parameter, [ValidatedNotNull] TCollection items, Func exceptionFactory) where TCollection : class, IEnumerable + public static TItem MustBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] TCollection items, Func exceptionFactory) where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (items is null || !parameter.IsOneOf(items)) Throw.CustomException(exceptionFactory, parameter, items!); return parameter; @@ -251,7 +254,8 @@ public static TItem MustBeOneOf(this TItem parameter, [Valid /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("items:null => halt")] - public static TItem MustNotBeOneOf(this TItem parameter, [ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static TItem MustNotBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { // ReSharper disable PossibleMultipleEnumeration if (parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) @@ -269,9 +273,9 @@ public static TItem MustNotBeOneOf(this TItem parameter, [ValidatedNotNul /// Your custom exception thrown when is equal to one of the specified , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("items:null => halt")] - public static TItem MustNotBeOneOf(this TItem parameter, [ValidatedNotNull] TCollection items, Func exceptionFactory) where TCollection : class, IEnumerable + public static TItem MustNotBeOneOf(this TItem parameter, [NotNull, ValidatedNotNull] TCollection items, Func exceptionFactory) where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (items is null || parameter.IsOneOf(items)) Throw.CustomException(exceptionFactory, parameter, items!); return parameter; @@ -288,11 +292,11 @@ public static TItem MustNotBeOneOf(this TItem parameter, [Va /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable + public static TCollection MustHaveMinimumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable { if (parameter.Count(parameterName, message) < count) - Throw.InvalidMinimumCollectionCount(parameter!, count, parameterName, message); - return parameter!; + Throw.InvalidMinimumCollectionCount(parameter, count, parameterName, message); + return parameter; } /// @@ -304,7 +308,7 @@ public static TCollection MustHaveMinimumCount([ValidatedNotNull] t /// Your custom exception thrown when does not contain at least the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) where TCollection : class, IEnumerable + public static TCollection MustHaveMinimumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) where TCollection : class, IEnumerable { if (parameter is null || parameter.Count() < count) Throw.CustomException(exceptionFactory, parameter, count); @@ -322,11 +326,11 @@ public static TCollection MustHaveMinimumCount([ValidatedNotNull] t /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable + public static TCollection MustHaveMaximumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where TCollection : class, IEnumerable { if (parameter.Count(parameterName, message) > count) - Throw.InvalidMaximumCollectionCount(parameter!, count, parameterName, message); - return parameter!; + Throw.InvalidMaximumCollectionCount(parameter, count, parameterName, message); + return parameter; } /// @@ -338,7 +342,7 @@ public static TCollection MustHaveMaximumCount([ValidatedNotNull] t /// Your custom exception thrown when does not contain at most the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) where TCollection : class, IEnumerable + public static TCollection MustHaveMaximumCount([NotNull, ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) where TCollection : class, IEnumerable { if (parameter is null || parameter.Count() > count) Throw.CustomException(exceptionFactory, parameter, count); diff --git a/Code/Light.GuardClauses/Check.StringAssertions.cs b/Code/Light.GuardClauses/Check.StringAssertions.cs index 31815fb..e8fff60 100644 --- a/Code/Light.GuardClauses/Check.StringAssertions.cs +++ b/Code/Light.GuardClauses/Check.StringAssertions.cs @@ -5,6 +5,7 @@ using Light.GuardClauses.Exceptions; using Light.GuardClauses.FrameworkExtensions; using System.Runtime.CompilerServices; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; @@ -28,7 +29,7 @@ public static partial class Check /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrEmpty([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter is null) Throw.ArgumentNull(parameterName, message); @@ -46,16 +47,12 @@ public static string MustNotBeNullOrEmpty([ValidatedNotNull] this string? parame /// Your custom exception thrown when is an empty string or null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static string MustNotBeNullOrEmpty([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustNotBeNullOrEmpty([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (string.IsNullOrEmpty(parameter)) + if (parameter.IsNullOrEmpty()) Throw.CustomException(exceptionFactory, parameter); - -#if NETSTANDARD2_0 - return parameter!; -#else + return parameter; -#endif } /// @@ -77,11 +74,11 @@ public static string MustNotBeNullOrEmpty([ValidatedNotNull] this string? parame /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotBeNullOrWhiteSpace([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { parameter.MustNotBeNullOrEmpty(parameterName, message); - foreach (var character in parameter!) + foreach (var character in parameter) { if (!character.IsWhiteSpace()) return parameter; @@ -99,7 +96,7 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// Your custom exception thrown when is null, empty, or contains only white space. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory: null => halt")] - public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustNotBeNullOrWhiteSpace([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { if (parameter.IsNullOrWhiteSpace()) Throw.CustomException(exceptionFactory, parameter); @@ -158,10 +155,10 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// Your custom exception thrown when is not equal to . /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) + public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) { if (!string.Equals(parameter, other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); return parameter; } @@ -174,7 +171,7 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// Thrown when is not equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { @@ -191,11 +188,11 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The enum value specifying how the two strings should be compared. /// The delegate that creates your custom exception. and are passed to this delegate. /// Your custom exception thrown when is not equal to . - /// Thrown when is not a valid value from the enum. - public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + /// Thrown when is not a valid value from the enum. + public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) { if (!parameter.Equals(other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); return parameter; } @@ -223,7 +220,7 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The first string to be compared. /// The second string to be compared. /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. + /// The delegate that creates your custom exception. , , and are passed to this delegate. /// Your custom exception thrown when is equal to . /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -243,7 +240,7 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// Thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { @@ -258,14 +255,14 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// The first string to be compared. /// The second string to be compared. /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. + /// The delegate that creates your custom exception. , , and are passed to this delegate. /// Your custom exception thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) { if (parameter.Equals(other, comparisonType)) - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); return parameter; } @@ -280,11 +277,11 @@ public static string MustNotBeNullOrWhiteSpace([ValidatedNotNull] this string? p /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; regex:null => halt")] - public static string MustMatch([ValidatedNotNull] this string? parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustMatch([NotNull, ValidatedNotNull] this string? parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!regex.MustNotBeNull(nameof(regex), message).IsMatch(parameter.MustNotBeNull(parameterName, message))) - Throw.StringDoesNotMatch(parameter!, regex, parameterName, message); - return parameter!; + Throw.StringDoesNotMatch(parameter, regex, parameterName, message); + return parameter; } /// @@ -299,7 +296,7 @@ public static string MustMatch([ValidatedNotNull] this string? parameter, Regex /// or when is null. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string MustMatch([ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) + public static string MustMatch([NotNull, ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || regex is null || !regex.IsMatch(parameter)) @@ -340,11 +337,13 @@ public static bool Equals(this string? @string, string? value, StringComparisonT /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([ValidatedNotNull] this string? parameter, string? value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string? value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) - Throw.StringDoesNotContain(parameter!, value!, parameterName, message); - return parameter!; + { + Throw.StringDoesNotContain(parameter, value, parameterName, message); + } + return parameter; } /// @@ -360,11 +359,13 @@ public static string MustContain([ValidatedNotNull] this string? parameter, stri /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || value is null || !parameter.Contains(value)) + { Throw.CustomException(exceptionFactory, parameter, value!); + } return parameter; } @@ -381,11 +382,13 @@ public static string MustContain([ValidatedNotNull] this string? parameter, stri /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) < 0) - Throw.StringDoesNotContain(parameter!, value, comparisonType, parameterName, message); - return parameter!; + { + Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); + } + return parameter; } /// @@ -398,16 +401,18 @@ public static string MustContain([ValidatedNotNull] this string? parameter, stri /// /// Your custom exception thrown when does not contain , /// or when is null, - /// or when is null, - /// or when is not a valid value from the enum. + /// or when is null. /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static string MustContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || parameter.IndexOf(value, comparisonType) < 0) + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) + { Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } return parameter; } @@ -422,11 +427,13 @@ public static string MustContain([ValidatedNotNull] this string? parameter, stri /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) - Throw.StringContains(parameter!, value, parameterName, message); - return parameter!; + { + Throw.StringContains(parameter, value, parameterName, message); + } + return parameter; } /// @@ -434,7 +441,7 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// /// The string to be checked. /// The string that must not be part of . - /// The delegate that creates your custom exception (optional). and are passed to this + /// The delegate that creates your custom exception (optional). and are passed to this delegate. /// /// Your custom exception thrown when contains , /// or when is null, @@ -442,7 +449,7 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || value is null || parameter.Contains(value)) @@ -463,11 +470,11 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) >= 0) - Throw.StringContains(parameter!, value, comparisonType, parameterName, message); - return parameter!; + Throw.StringContains(parameter, value, comparisonType, parameterName, message); + return parameter; } /// @@ -480,15 +487,15 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// /// Your custom exception thrown when contains , /// or when is null, - /// or when is null, - /// or when is not a valid value of the enum. + /// or when is null. /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static string MustNotContain([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || parameter.IndexOf(value, comparisonType) >= 0) + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) >= 0) Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); return parameter; } @@ -504,7 +511,8 @@ public static string MustNotContain([ValidatedNotNull] this string? parameter, s /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("string:null => halt; value:null => halt")] - public static bool Contains([ValidatedNotNull] this string @string, string value, StringComparison comparisonType) => + // ReSharper disable once RedundantNullableFlowAttribute + public static bool Contains([NotNull, ValidatedNotNull] this string @string, string value, StringComparison comparisonType) => @string.MustNotBeNull(nameof(@string)).IndexOf(value.MustNotBeNull(nameof(value)), comparisonType) >= 0; /// @@ -516,8 +524,10 @@ public static bool Contains([ValidatedNotNull] this string @string, string value /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("value:null => halt; other:null => halt")] - public static bool IsSubstringOf([ValidatedNotNull] this string value, [ValidatedNotNull] string other) => + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf([NotNull, ValidatedNotNull] this string value, [NotNull, ValidatedNotNull] string other) => other.MustNotBeNull(nameof(other)).Contains(value); + // ReSharper restore RedundantNullableFlowAttribute /// /// Checks if the string is a substring of the other string. @@ -530,8 +540,10 @@ public static bool IsSubstringOf([ValidatedNotNull] this string value, [Validate /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("value:null => halt; other:null => halt")] - public static bool IsSubstringOf(this string value, string other, StringComparison comparisonType) => + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf([NotNull, ValidatedNotNull] this string value, [NotNull, ValidatedNotNull] string other, StringComparison comparisonType) => other.MustNotBeNull(nameof(other)).IndexOf(value, comparisonType) != -1; + // ReSharper disable RedundantNullableFlowAttribute /// /// Ensures that the string is a substring of the specified other string, or otherwise throws a . @@ -544,11 +556,11 @@ public static bool IsSubstringOf(this string value, string other, StringComparis /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) - Throw.NotSubstring(parameter!, value, parameterName, message); - return parameter!; + Throw.NotSubstring(parameter, value, parameterName, message); + return parameter; } /// @@ -564,7 +576,7 @@ public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || value is null || !value.Contains(parameter)) @@ -585,11 +597,11 @@ public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (value.MustNotBeNull(nameof(value), message).IndexOf(parameter.MustNotBeNull(parameterName, message), comparisonType) == -1) - Throw.NotSubstring(parameter!, value, comparisonType, parameterName, message); - return parameter!; + Throw.NotSubstring(parameter, value, comparisonType, parameterName, message); + return parameter; } /// @@ -602,15 +614,15 @@ public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter /// /// Your custom exception thrown when does not contain , /// or when is null, - /// or when is null, - /// or when is not a valid value of the enum. + /// or when is null. /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static string MustBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || value.IndexOf(parameter, comparisonType) == -1) + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) == -1) Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); return parameter; } @@ -626,11 +638,11 @@ public static string MustBeSubstringOf([ValidatedNotNull] this string? parameter /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) - Throw.Substring(parameter!, value, parameterName, message); - return parameter!; + Throw.Substring(parameter, value, parameterName, message); + return parameter; } /// @@ -647,7 +659,7 @@ public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parame /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (parameter is null || value is null || value.Contains(parameter)) @@ -668,11 +680,11 @@ public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parame /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (value.MustNotBeNull(nameof(value), message).IndexOf(parameter.MustNotBeNull(parameterName, message), comparisonType) != -1) - Throw.Substring(parameter!, value, comparisonType, parameterName, message); - return parameter!; + Throw.Substring(parameter, value, comparisonType, parameterName, message); + return parameter; } /// @@ -681,7 +693,7 @@ public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parame /// The string to be checked. /// The other string that must not contain . /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. and are passed to this delegate. + /// The delegate that creates your custom exception. , , and are passed to this delegate. /// /// Your custom exception thrown when contains , /// or when is null, @@ -690,11 +702,344 @@ public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parame /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static string MustNotBeSubstringOf([NotNull, ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || value.IndexOf(parameter, comparisonType) != -1) + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) != -1) + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + return parameter; + } + + /// + /// Ensures that the string starts with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must start with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not start with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType = StringComparison.CurrentCulture, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + { + Throw.StringDoesNotStartWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string must start with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.StartsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; + } + + /// + /// Ensures that the string starts with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string must start with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !comparisonType.IsValidEnumValue() || !parameter.StartsWith(value, comparisonType)) + { Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; + } + + /// + /// Ensures that the string does not start with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when starts with . + /// Thrown when or is null. + public static string MustNotStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType = StringComparison.CurrentCulture, + [CallerArgumentExpression("parameter")] + string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + { + Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must not start with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; + } + + /// + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// + /// The string to be checked. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; + } + + /// + /// Ensures that the string ends with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not end with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + public static string MustEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType = StringComparison.CurrentCulture, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + { + Throw.StringDoesNotEndWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that the string ends with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must end with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; + } + + /// + /// Ensures that the string ends with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + return parameter; + } + + /// + /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must not end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when ends with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + public static string MustNotEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType = StringComparison.CurrentCulture, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + { + Throw.StringEndsWith(parameter, value, comparisonType, parameterName, message); + } + return parameter; + } + + /// + /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must not end with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when ends with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + return parameter; + } + + /// + /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// + /// The string to be checked. + /// The other string must not end with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when ends with , + /// or when is null, + /// or when is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith( + [NotNull, ValidatedNotNull] this string? parameter, + [NotNull, ValidatedNotNull] string value, + StringComparison comparisonType, + [NotNull, ValidatedNotNull] Func exceptionFactory + ) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value, comparisonType)) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } return parameter; } @@ -730,11 +1075,11 @@ public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) - Throw.InvalidEmailAddress(parameter!, parameterName, message); - return parameter!; + Throw.InvalidEmailAddress(parameter, parameterName, message); + return parameter; } /// @@ -746,7 +1091,7 @@ public static string MustBeEmailAddress([ValidatedNotNull] this string? paramete /// Your custom exception thrown when is null or no valid email address. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { if (!parameter.IsEmailAddress()) Throw.CustomException(exceptionFactory, parameter); @@ -765,11 +1110,11 @@ public static string MustBeEmailAddress([ValidatedNotNull] this string? paramete /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress(emailAddressPattern)) - Throw.InvalidEmailAddress(parameter!, parameterName, message); - return parameter!; + Throw.InvalidEmailAddress(parameter, parameterName, message); + return parameter; } /// @@ -782,7 +1127,7 @@ public static string MustBeEmailAddress([ValidatedNotNull] this string? paramete /// Your custom exception thrown when is null or no valid email address. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) + public static string MustBeEmailAddress([NotNull, ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) @@ -801,11 +1146,11 @@ public static string MustBeEmailAddress([ValidatedNotNull] this string? paramete /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeShorterThan([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length >= length) - Throw.StringNotShorterThan(parameter!, length, parameterName, message); - return parameter!; + Throw.StringNotShorterThan(parameter, length, parameterName, message); + return parameter; } /// @@ -817,7 +1162,7 @@ public static string MustBeShorterThan([ValidatedNotNull] this string? parameter /// Your custom exception thrown when is null or when it has a length greater than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeShorterThan([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length >= length) Throw.CustomException(exceptionFactory, parameter, length); @@ -835,11 +1180,11 @@ public static string MustBeShorterThan([ValidatedNotNull] this string? parameter /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeShorterThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length > length) - Throw.StringNotShorterThanOrEqualTo(parameter!, length, parameterName, message); - return parameter!; + Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); + return parameter; } /// @@ -851,7 +1196,7 @@ public static string MustBeShorterThanOrEqualTo([ValidatedNotNull] this string? /// Your custom exception thrown when is null or when it has a length greater than . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeShorterThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length > length) Throw.CustomException(exceptionFactory, parameter, length); @@ -865,15 +1210,15 @@ public static string MustBeShorterThanOrEqualTo([ValidatedNotNull] this string? /// The asserted length of the string. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length different than . + /// Thrown when has a length other than . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustHaveLength([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length != length) - Throw.StringLengthNotEqualTo(parameter!, length, parameterName, message); - return parameter!; + Throw.StringLengthNotEqualTo(parameter, length, parameterName, message); + return parameter; } /// @@ -882,10 +1227,10 @@ public static string MustHaveLength([ValidatedNotNull] this string? parameter, i /// The string to be checked. /// The asserted length of the string. /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length different than . + /// Your custom exception thrown when is null or when it has a length other than . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustHaveLength([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length != length) Throw.CustomException(exceptionFactory, parameter, length); @@ -903,11 +1248,11 @@ public static string MustHaveLength([ValidatedNotNull] this string? parameter, i /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThan([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeLongerThan([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length <= length) - Throw.StringNotLongerThan(parameter!, length, parameterName, message); - return parameter!; + Throw.StringNotLongerThan(parameter, length, parameterName, message); + return parameter; } /// @@ -919,7 +1264,7 @@ public static string MustBeLongerThan([ValidatedNotNull] this string? parameter, /// Your custom exception thrown when is null or when it has a length shorter than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThan([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeLongerThan([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length <= length) Throw.CustomException(exceptionFactory, parameter, length); @@ -937,11 +1282,11 @@ public static string MustBeLongerThan([ValidatedNotNull] this string? parameter, /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeLongerThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).Length < length) - Throw.StringNotLongerThanOrEqualTo(parameter!, length, parameterName, message); - return parameter!; + Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); + return parameter; } /// @@ -953,7 +1298,7 @@ public static string MustBeLongerThanOrEqualTo([ValidatedNotNull] this string? p /// Your custom exception thrown when is null or when it has a length shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustBeLongerThanOrEqualTo([NotNull, ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { if (parameter is null || parameter.Length < length) Throw.CustomException(exceptionFactory, parameter, length); @@ -971,11 +1316,11 @@ public static string MustBeLongerThanOrEqualTo([ValidatedNotNull] this string? p /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([ValidatedNotNull] this string? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustHaveLengthIn([NotNull, ValidatedNotNull] this string? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) - Throw.StringLengthNotInRange(parameter!, range, parameterName, message); - return parameter!; + Throw.StringLengthNotInRange(parameter, range, parameterName, message); + return parameter; } /// @@ -987,7 +1332,7 @@ public static string MustHaveLengthIn([ValidatedNotNull] this string? parameter, /// Your custom exception thrown when is null or its length is not within the specified range. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) + public static string MustHaveLengthIn([NotNull, ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) { if (parameter is null || !range.IsValueWithinRange(parameter.Length)) Throw.CustomException(exceptionFactory, parameter, range); @@ -1012,11 +1357,11 @@ public static string MustHaveLengthIn([ValidatedNotNull] this string? parameter, /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeNewLine([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) - Throw.NotNewLine(parameter!, parameterName, message); - return parameter!; + Throw.NotNewLine(parameter, parameterName, message); + return parameter; } /// @@ -1027,7 +1372,7 @@ public static string MustBeNewLine([ValidatedNotNull] this string? parameter, [C /// Your custom exception thrown when is not equal to "\n" or "\r\n". [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeNewLine([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { if (!parameter.IsNewLine()) Throw.CustomException(exceptionFactory, parameter); @@ -1081,11 +1426,11 @@ public static bool IsTrimmed(this ReadOnlySpan parameter) => /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeTrimmed([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsTrimmed()) Throw.NotTrimmed(parameter, parameterName, message); - return parameter!; + return parameter; } @@ -1098,11 +1443,11 @@ public static string MustBeTrimmed([ValidatedNotNull] this string? parameter, [C /// Your custom exception thrown when is null or not trimmed. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeTrimmed([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsTrimmed(regardNullAsTrimmed: false)) + if (parameter is null || !parameter.AsSpan().IsTrimmed()) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } /// @@ -1149,11 +1494,11 @@ public static bool IsTrimmedAtStart(this ReadOnlySpan parameter) => /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeTrimmedAtStart([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) Throw.NotTrimmedAtStart(parameter, parameterName, message); - return parameter!; + return parameter; } /// @@ -1165,11 +1510,11 @@ public static string MustBeTrimmedAtStart([ValidatedNotNull] this string? parame /// Your custom exception thrown when is null or not trimmed at the start. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeTrimmedAtStart([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsTrimmedAtStart(regardNullAsTrimmed: false)) + if (parameter is null || !parameter.AsSpan().IsTrimmedAtStart()) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } /// @@ -1216,11 +1561,11 @@ public static bool IsTrimmedAtEnd(this ReadOnlySpan parameter) => /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeTrimmedAtEnd([NotNull, ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtEnd()) Throw.NotTrimmedAtEnd(parameter, parameterName, message); - return parameter!; + return parameter; } /// @@ -1232,10 +1577,10 @@ public static string MustBeTrimmedAtEnd([ValidatedNotNull] this string? paramete /// Your custom exception thrown when is null or not trimmed at the end. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeTrimmedAtEnd([NotNull, ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsTrimmedAtEnd(regardNullAsTrimmed: false)) + if (parameter is null || !parameter.AsSpan().IsTrimmedAtEnd()) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } } \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.TypeAssertions.cs b/Code/Light.GuardClauses/Check.TypeAssertions.cs index a858cc8..3fd8a0d 100644 --- a/Code/Light.GuardClauses/Check.TypeAssertions.cs +++ b/Code/Light.GuardClauses/Check.TypeAssertions.cs @@ -5,6 +5,7 @@ #if NET8_0 using System.Diagnostics.CodeAnalysis; #endif +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; @@ -48,9 +49,10 @@ public static bool Implements( #if NET8_0 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif - [ValidatedNotNull] - this Type type, - [ValidatedNotNull] Type interfaceType + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, + [NotNull, ValidatedNotNull] Type interfaceType + // ReSharper restore RedundantNullableFlowAttribute ) { type.MustNotBeNull(); @@ -79,10 +81,11 @@ public static bool Implements( #if NET8_0 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif - [ValidatedNotNull] - this Type type, - [ValidatedNotNull] Type interfaceType, - [ValidatedNotNull] IEqualityComparer typeComparer + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, + [NotNull, ValidatedNotNull] Type interfaceType, + [NotNull, ValidatedNotNull] IEqualityComparer typeComparer + // ReSharper restore RedundantNullableFlowAttribute ) { type.MustNotBeNull(); @@ -112,8 +115,10 @@ public static bool IsOrImplements( #if NET8_0 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif - [ValidatedNotNull] this Type type, - [ValidatedNotNull] Type otherType) => + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, + [NotNull, ValidatedNotNull] Type otherType) => + // ReSharper restore RedundantNullableFlowAttribute type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType); /// @@ -131,9 +136,11 @@ public static bool IsOrImplements( #if NET8_0 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif - [ValidatedNotNull] this Type type, - [ValidatedNotNull] Type otherType, - [ValidatedNotNull] IEqualityComparer typeComparer) => + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, + [NotNull, ValidatedNotNull] Type otherType, + [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => + // ReSharper restore RedundantNullableFlowAttribute typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type.MustNotBeNull(nameof(type)), otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType, typeComparer); /// @@ -144,7 +151,9 @@ public static bool IsOrImplements( /// The base class that should derive from. /// Thrown when or is null. [ContractAnnotation("type:null => halt; baseClass:null => halt")] - public static bool DerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type baseClass) + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool DerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClass) + // ReSharper restore RedundantNullableFlowAttribute { baseClass.MustNotBeNull(nameof(baseClass)); @@ -169,7 +178,9 @@ public static bool DerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotN /// The equality comparer used to compare the types. /// Thrown when , or , or is null. [ContractAnnotation("type:null => halt; baseClass:null => halt; typeComparer:null => halt")] - public static bool DerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type baseClass, [ValidatedNotNull] IEqualityComparer typeComparer) + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool DerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type baseClass, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) + // ReSharper restore RedundantNullableFlowAttribute { baseClass.MustNotBeNull(nameof(baseClass)); typeComparer.MustNotBeNull(nameof(typeComparer)); @@ -195,7 +206,9 @@ public static bool DerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotN /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrDerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType) => + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool IsOrDerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType) => + // ReSharper restore RedundantNullableFlowAttribute type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType); /// @@ -208,7 +221,9 @@ public static bool IsOrDerivesFrom([ValidatedNotNull] this Type type, [Validated /// Thrown when , or , or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] - public static bool IsOrDerivesFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType, [ValidatedNotNull] IEqualityComparer typeComparer) => + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool IsOrDerivesFrom([NotNull, ValidatedNotNull] this Type type, [NotNull, ValidatedNotNull] Type otherType, [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => + // ReSharper restore RedundantNullableFlowAttribute typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType, typeComparer); @@ -224,9 +239,11 @@ public static bool IsOrDerivesFrom([ValidatedNotNull] this Type type, [Validated public static bool InheritsFrom( #if NET8_0 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [ValidatedNotNull] this Type type, - [ValidatedNotNull] Type baseClassOrInterfaceType) => +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, + [NotNull, ValidatedNotNull] Type baseClassOrInterfaceType) => + // ReSharper restore RedundantNullableFlowAttribute baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)) .IsInterface ? type.Implements(baseClassOrInterfaceType) : @@ -245,10 +262,12 @@ public static bool InheritsFrom( public static bool InheritsFrom( #if NET8_0 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [ValidatedNotNull] this Type type, - [ValidatedNotNull] Type baseClassOrInterfaceType, - [ValidatedNotNull] IEqualityComparer typeComparer) => +#endif + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, + [NotNull, ValidatedNotNull] Type baseClassOrInterfaceType, + [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => + // ReSharper restore RedundantNullableFlowAttribute baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)) .IsInterface ? type.Implements(baseClassOrInterfaceType, typeComparer) : @@ -268,8 +287,10 @@ public static bool IsOrInheritsFrom( #if NET8_0 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif - [ValidatedNotNull] this Type type, - [ValidatedNotNull] Type otherType) => + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, + [NotNull, ValidatedNotNull] Type otherType) => + // ReSharper restore RedundantNullableFlowAttribute type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType); @@ -287,9 +308,11 @@ public static bool IsOrInheritsFrom( #if NET8_0 [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif - [ValidatedNotNull] this Type type, - [ValidatedNotNull] Type otherType, - [ValidatedNotNull] IEqualityComparer typeComparer) => + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + [NotNull, ValidatedNotNull] this Type type, + [NotNull, ValidatedNotNull] Type otherType, + [NotNull, ValidatedNotNull] IEqualityComparer typeComparer) => + // ReSharper restore RedundantNullableFlowAttribute typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType, typeComparer); @@ -301,7 +324,8 @@ public static bool IsOrInheritsFrom( /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("type:null => halt")] - public static bool IsOpenConstructedGenericType([ValidatedNotNull] this Type type) => + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool IsOpenConstructedGenericType([NotNull, ValidatedNotNull] this Type type) => type.MustNotBeNull(nameof(type)).IsGenericType && type.ContainsGenericParameters && type.IsGenericTypeDefinition == false; diff --git a/Code/Light.GuardClauses/Check.UriAssertions.cs b/Code/Light.GuardClauses/Check.UriAssertions.cs index b894e1e..a45d6c8 100644 --- a/Code/Light.GuardClauses/Check.UriAssertions.cs +++ b/Code/Light.GuardClauses/Check.UriAssertions.cs @@ -4,6 +4,7 @@ using JetBrains.Annotations; using Light.GuardClauses.Exceptions; using System.Runtime.CompilerServices; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses; @@ -19,11 +20,11 @@ public static partial class Check /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustBeAbsoluteUri([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) - Throw.MustBeAbsoluteUri(parameter!, parameterName, message); - return parameter!; + Throw.MustBeAbsoluteUri(parameter, parameterName, message); + return parameter; } /// @@ -34,7 +35,7 @@ public static Uri MustBeAbsoluteUri([ValidatedNotNull] this Uri? parameter, [Cal /// Your custom exception thrown when is not an absolute URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static Uri MustBeAbsoluteUri([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { if (parameter is null || parameter.IsAbsoluteUri == false) Throw.CustomException(exceptionFactory, parameter); @@ -51,11 +52,11 @@ public static Uri MustBeAbsoluteUri([ValidatedNotNull] this Uri? parameter, Func /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustBeRelativeUri([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri) - Throw.MustBeRelativeUri(parameter!, parameterName, message); - return parameter!; + Throw.MustBeRelativeUri(parameter, parameterName, message); + return parameter; } /// @@ -66,7 +67,7 @@ public static Uri MustBeRelativeUri([ValidatedNotNull] this Uri? parameter, [Cal /// Your custom exception thrown when is an absolute URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static Uri MustBeRelativeUri([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { if (parameter is null || parameter.IsAbsoluteUri) Throw.CustomException(exceptionFactory, parameter); @@ -85,11 +86,11 @@ public static Uri MustBeRelativeUri([ValidatedNotNull] this Uri? parameter, Func /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) - Throw.UriMustHaveScheme(parameter!, scheme, parameterName, message); - return parameter!; + Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); + return parameter; } /// @@ -105,11 +106,11 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) { if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } /// @@ -125,7 +126,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + public static Uri MustHaveScheme([NotNull, ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) { if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) Throw.CustomException(exceptionFactory, parameter, scheme); @@ -143,7 +144,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("https", parameterName, message); + public static Uri MustBeHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("https", parameterName, message); /// /// Ensures that the specified URI has the "https" scheme, or otherwise throws your custom exception. @@ -157,7 +158,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("https", exceptionFactory); + public static Uri MustBeHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("https", exceptionFactory); /// /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . @@ -170,7 +171,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("http", parameterName, message); + public static Uri MustBeHttpUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("http", parameterName, message); /// /// Ensures that the specified URI has the "http" scheme, or otherwise throws your custom exception. @@ -184,7 +185,7 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("http", exceptionFactory); + public static Uri MustBeHttpUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("http", exceptionFactory); /// /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws an . @@ -197,11 +198,11 @@ public static Uri MustHaveScheme([ValidatedNotNull] this Uri? parameter, string /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustBeHttpOrHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter!.Scheme.Equals("http") == false) + if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) Throw.UriMustHaveOneSchemeOf(parameter, new[] { "https", "http" }, parameterName, message); - return parameter!; + return parameter; } /// @@ -216,11 +217,11 @@ public static Uri MustBeHttpOrHttpsUrl([ValidatedNotNull] this Uri? parameter, [ /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static Uri MustBeHttpOrHttpsUrl([NotNull, ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter!.Scheme.Equals("http") == false) + if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) Throw.CustomException(exceptionFactory, parameter); - return parameter!; + return parameter; } /// @@ -235,19 +236,19 @@ public static Uri MustBeHttpOrHttpsUrl([ValidatedNotNull] this Uri? parameter, F /// Throw when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; schemes:null => halt")] - public static Uri MustHaveOneSchemeOf([ValidatedNotNull] this Uri? parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustHaveOneSchemeOf([NotNull, ValidatedNotNull] this Uri? parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { // ReSharper disable PossibleMultipleEnumeration parameter.MustBeAbsoluteUri(parameterName, message); if (schemes is ICollection collection) { - if (!collection.Contains(parameter!.Scheme)) + if (!collection.Contains(parameter.Scheme)) Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); return parameter; } - if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter!.Scheme)) + if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter.Scheme)) Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); return parameter; // ReSharper restore PossibleMultipleEnumeration @@ -267,7 +268,7 @@ public static Uri MustHaveOneSchemeOf([ValidatedNotNull] this Uri? parameter, IE /// Throw when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveOneSchemeOf([ValidatedNotNull] this Uri? parameter, TCollection schemes, Func exceptionFactory) where TCollection : class, IEnumerable + public static Uri MustHaveOneSchemeOf([NotNull, ValidatedNotNull] this Uri? parameter, TCollection schemes, Func exceptionFactory) where TCollection : class, IEnumerable { if (parameter is null || !parameter.IsAbsoluteUri) Throw.CustomException(exceptionFactory, parameter, schemes); @@ -279,7 +280,7 @@ public static Uri MustHaveOneSchemeOf([ValidatedNotNull] this Uri? return parameter; } - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - caller might have NRTs turned off + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off if (schemes is null || !schemes.Contains(parameter.Scheme)) Throw.CustomException(exceptionFactory, parameter, schemes!); return parameter; diff --git a/Code/Light.GuardClauses/Exceptions/Throw.cs b/Code/Light.GuardClauses/Exceptions/Throw.cs index b2052c1..5b3bbf9 100644 --- a/Code/Light.GuardClauses/Exceptions/Throw.cs +++ b/Code/Light.GuardClauses/Exceptions/Throw.cs @@ -268,9 +268,41 @@ public static void Substring(string parameter, string other, [CallerArgumentExpr [DoesNotReturn] public static void Substring(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + + /// + /// Throws the default indicating that a string does not start with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotStartWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => + throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + + /// + /// Throws the default indicating that a string does start with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringStartsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => + throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + + /// + /// Throws the default indicating that a string does not end with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotEndWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => + throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + + /// + /// Throws the default indicating that a string ends with another one, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringEndsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => + throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string is not shorter than the given length, using the optional parameter name an message. + /// Throws the default indicating that a string is not shorter than the given length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] @@ -278,7 +310,7 @@ public static void StringNotShorterThan(string parameter, int length, [CallerArg throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter than {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string is not shorter or equal to the given length, using the optional parameter name an message. + /// Throws the default indicating that a string is not shorter or equal to the given length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] @@ -286,7 +318,7 @@ public static void StringNotShorterThanOrEqualTo(string parameter, int length, [ throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be shorter or equal to {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string has a different length than the specified one, using the optional parameter name an message. + /// Throws the default indicating that a string has a different length than the specified one, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] @@ -294,7 +326,7 @@ public static void StringLengthNotEqualTo(string parameter, int length, [CallerA throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must have length {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string is not longer than the given length, using the optional parameter name an message. + /// Throws the default indicating that a string is not longer than the given length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] @@ -302,7 +334,7 @@ public static void StringNotLongerThan(string parameter, int length, [CallerArgu throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string is not longer or equal to the given length, using the optional parameter name an message. + /// Throws the default indicating that a string is not longer or equal to the given length, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] @@ -310,7 +342,7 @@ public static void StringNotLongerThanOrEqualTo(string parameter, int length, [C throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must be longer than or equal to {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a string's length is not in between the given range, using the optional parameter name an message. + /// Throws the default indicating that a string's length is not in between the given range, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] diff --git a/Code/Light.GuardClauses/FrameworkExtensions/EnumerableExtensions.cs b/Code/Light.GuardClauses/FrameworkExtensions/EnumerableExtensions.cs index a6f66e2..f204458 100644 --- a/Code/Light.GuardClauses/FrameworkExtensions/EnumerableExtensions.cs +++ b/Code/Light.GuardClauses/FrameworkExtensions/EnumerableExtensions.cs @@ -5,6 +5,7 @@ using JetBrains.Annotations; using System.Runtime.CompilerServices; using Light.GuardClauses.Exceptions; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses.FrameworkExtensions; @@ -23,7 +24,8 @@ public static class EnumerableExtensions /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull")] - public static IList AsList([ValidatedNotNull] this IEnumerable source) => + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable source) => source as IList ?? source.ToList(); /// @@ -37,7 +39,8 @@ public static IList AsList([ValidatedNotNull] this IEnumerable source) /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] - public static IList AsList([ValidatedNotNull] this IEnumerable source, Func, IList> createCollection) => + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IList AsList([NotNull, ValidatedNotNull] this IEnumerable source, Func, IList> createCollection) => source as IList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); /// @@ -49,7 +52,8 @@ public static IList AsList([ValidatedNotNull] this IEnumerable source, /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull")] - public static T[] AsArray([ValidatedNotNull] this IEnumerable source) => source as T[] ?? source.ToArray(); + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static T[] AsArray([NotNull, ValidatedNotNull] this IEnumerable source) => source as T[] ?? source.ToArray(); /// /// Performs the action on each item of the specified enumerable. If the enumerable contains items that are null, this @@ -61,7 +65,8 @@ public static IList AsList([ValidatedNotNull] this IEnumerable source, /// The value indicating whether this method should throw a when any of the items is null (optional). Defaults to true. /// Thrown when or is null. /// Thrown when contains a value that is null and is set to true. - public static IEnumerable ForEach([ValidatedNotNull] this IEnumerable enumerable, Action action, bool throwWhenItemIsNull = true) + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IEnumerable ForEach([NotNull, ValidatedNotNull] this IEnumerable enumerable, Action action, bool throwWhenItemIsNull = true) { // ReSharper disable PossibleMultipleEnumeration action.MustNotBeNull(nameof(action)); @@ -111,7 +116,8 @@ public static IEnumerable ForEach([ValidatedNotNull] this IEnumerable e /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull")] - public static IReadOnlyList AsReadOnlyList([ValidatedNotNull] this IEnumerable source) => + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] this IEnumerable source) => source as IReadOnlyList ?? source.ToList(); /// @@ -125,8 +131,11 @@ public static IReadOnlyList AsReadOnlyList([ValidatedNotNull] this IEnumer /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] - public static IReadOnlyList AsReadOnlyList([ValidatedNotNull] this IEnumerable source, Func, IReadOnlyList> createCollection) => + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IReadOnlyList AsReadOnlyList([NotNull, ValidatedNotNull] this IEnumerable source, [NotNull, ValidatedNotNull] Func, IReadOnlyList> createCollection) => source as IReadOnlyList ?? createCollection.MustNotBeNull(nameof(createCollection))(source.MustNotBeNull(nameof(source))); + // ReSharper restore RedundantNullableFlowAttribute + /// /// Gets the count of the specified enumerable. @@ -135,7 +144,8 @@ public static IReadOnlyList AsReadOnlyList([ValidatedNotNull] this IEnumer /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] - public static int Count([ValidatedNotNull] this IEnumerable enumerable) + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static int Count([NotNull, ValidatedNotNull] this IEnumerable enumerable) { if (enumerable is ICollection collection) return collection.Count; @@ -154,7 +164,7 @@ public static int Count([ValidatedNotNull] this IEnumerable enumerable) /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] - public static int Count([ValidatedNotNull] this IEnumerable? enumerable, string? parameterName, string? message) + public static int Count([NotNull, ValidatedNotNull] this IEnumerable? enumerable, string? parameterName, string? message) { if (enumerable is ICollection collection) return collection.Count; @@ -171,7 +181,8 @@ public static int Count([ValidatedNotNull] this IEnumerable? enumerable, string? /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] - public static int GetCount(this IEnumerable enumerable) + // ReSharper disable RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable enumerable) { if (enumerable is ICollection collection) return collection.Count; @@ -192,7 +203,7 @@ public static int GetCount(this IEnumerable enumerable) /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("enumerable:null => halt")] - public static int GetCount(this IEnumerable enumerable, string? parameterName, string? message = null) + public static int GetCount([NotNull, ValidatedNotNull] this IEnumerable enumerable, string? parameterName, string? message = null) { if (enumerable is ICollection collection) return collection.Count; @@ -227,15 +238,23 @@ private static int DetermineCountViaEnumerating(IEnumerable? enumerable) var enumerator = enumerable.MustNotBeNull(nameof(enumerable)).GetEnumerator(); while (enumerator.MoveNext()) count++; + if (enumerator is IDisposable disposable) + { + disposable.Dispose(); + } return count; } - private static int DetermineCountViaEnumerating(IEnumerable? enumerable, string? parameterName, string? message) + private static int DetermineCountViaEnumerating([NotNull] IEnumerable? enumerable, string? parameterName, string? message) { var count = 0; var enumerator = enumerable.MustNotBeNull(parameterName, message).GetEnumerator(); while (enumerator.MoveNext()) count++; + if (enumerator is IDisposable disposable) + { + disposable.Dispose(); + } return count; } diff --git a/Code/Light.GuardClauses/FrameworkExtensions/ExpressionExtensions.cs b/Code/Light.GuardClauses/FrameworkExtensions/ExpressionExtensions.cs index bb380fb..c695215 100644 --- a/Code/Light.GuardClauses/FrameworkExtensions/ExpressionExtensions.cs +++ b/Code/Light.GuardClauses/FrameworkExtensions/ExpressionExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; @@ -10,7 +11,7 @@ namespace Light.GuardClauses.FrameworkExtensions; public static class ExpressionExtensions { /// - /// Extracts the from a expression of the shape "object => object.Property". + /// Extracts the from an expression of the shape "object => object.Property". /// /// The object type. /// The type of the property. @@ -19,7 +20,8 @@ public static class ExpressionExtensions /// /// Throw when the is not of the shape "object => object.Property". /// - public static PropertyInfo ExtractProperty([ValidatedNotNull] this Expression> expression) + // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + public static PropertyInfo ExtractProperty([NotNull, ValidatedNotNull] this Expression> expression) { expression.MustNotBeNull(nameof(expression)); @@ -31,7 +33,7 @@ public static PropertyInfo ExtractProperty([ValidatedNotNull] this } /// - /// Extracts the from a expression of the shape "object => object.Field". + /// Extracts the from an expression of the shape "object => object.Field". /// /// The object type. /// The type of the field. @@ -40,7 +42,8 @@ public static PropertyInfo ExtractProperty([ValidatedNotNull] this /// /// Throw when the is not of the shape "object => object.Field". /// - public static FieldInfo ExtractField([ValidatedNotNull] this Expression> expression) + // ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + public static FieldInfo ExtractField([NotNull, ValidatedNotNull] this Expression> expression) { expression.MustNotBeNull(nameof(expression)); diff --git a/Code/Light.GuardClauses/FrameworkExtensions/TextExtensions.cs b/Code/Light.GuardClauses/FrameworkExtensions/TextExtensions.cs index 5a7cb2d..10f3b7c 100644 --- a/Code/Light.GuardClauses/FrameworkExtensions/TextExtensions.cs +++ b/Code/Light.GuardClauses/FrameworkExtensions/TextExtensions.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Text; using JetBrains.Annotations; +using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; namespace Light.GuardClauses.FrameworkExtensions; @@ -21,21 +22,22 @@ public static class TextExtensions /// Gets the list of types that will not be surrounded by quotation marks in error messages. /// public static readonly ReadOnlyCollection UnquotedTypes = - new ReadOnlyCollection(new[] - { - typeof(int), - typeof(long), - typeof(short), - typeof(sbyte), - typeof(uint), - typeof(ulong), - typeof(ushort), - typeof(byte), - typeof(bool), - typeof(double), - typeof(decimal), - typeof(float) - }); + new ( + [ + typeof(int), + typeof(long), + typeof(short), + typeof(sbyte), + typeof(uint), + typeof(ulong), + typeof(ushort), + typeof(byte), + typeof(bool), + typeof(double), + typeof(decimal), + typeof(float) + ] + ); private static bool IsUnquotedType() { @@ -83,11 +85,11 @@ private static bool IsUnquotedType() /// /// The value whose string representation is requested. [ContractAnnotation("value:null => halt; value:notnull => notnull")] - public static string? ToStringRepresentation([ValidatedNotNull] this T value) + public static string? ToStringRepresentation([NotNull, ValidatedNotNull] this T value) { value.MustNotBeNullReference(nameof(value)); - var content = value!.ToString(); + var content = value.ToString(); if (IsUnquotedType() || content.IsNullOrEmpty()) return content; @@ -95,7 +97,7 @@ private static bool IsUnquotedType() if (content.Length <= 126) { Span span = stackalloc char[content.Length + 2]; - span[0] = span[span.Length -1] = '"'; + span[0] = span[span.Length - 1] = '"'; content.AsSpan().CopyTo(span.Slice(1, content.Length)); return span.ToString(); } @@ -120,10 +122,12 @@ private static bool IsUnquotedType() /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendCollectionContent([ValidatedNotNull] this StringBuilder stringBuilder, [ValidatedNotNull] IEnumerable items, string headerLine = "Content of the collection:", bool finishWithNewLine = true) => + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendCollectionContent([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string headerLine = "Content of the collection:", bool finishWithNewLine = true) => stringBuilder.MustNotBeNull(nameof(stringBuilder)) .AppendLine(headerLine) .AppendItemsWithNewLine(items, finishWithNewLine: finishWithNewLine); + // ReSharper restore RedundantNullableFlowAttribute /// /// Appends the string representations of the specified items to the string builder. @@ -134,7 +138,9 @@ public static StringBuilder AppendCollectionContent([ValidatedNotNull] this S /// The text that is appended to the string builder when is empty. Defaults to "empty collection". /// Thrown when or is null. [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendItems([ValidatedNotNull] this StringBuilder stringBuilder, [ValidatedNotNull] IEnumerable items, string itemSeparator = ", ", string emptyCollectionText = "empty collection") + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendItems([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string itemSeparator = ", ", string emptyCollectionText = "empty collection") + // ReSharper restore RedundantNullableFlowAttribute { stringBuilder.MustNotBeNull(nameof(stringBuilder)); var list = items.MustNotBeNull(nameof(items)).AsList(); @@ -165,20 +171,23 @@ public static StringBuilder AppendItems([ValidatedNotNull] this StringBuilder /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendItemsWithNewLine([ValidatedNotNull] this StringBuilder stringBuilder, [ValidatedNotNull] IEnumerable items, string emptyCollectionText = "empty collection", bool finishWithNewLine = true) => + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendItemsWithNewLine([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] IEnumerable items, string emptyCollectionText = "empty collection", bool finishWithNewLine = true) => stringBuilder.AppendItems(items, DefaultNewLineSeparator, emptyCollectionText) .AppendLineIf(finishWithNewLine); + // ReSharper restore RedundantNullableFlowAttribute /// /// Appends the value to the specified string builder if the condition is true. /// /// The string builder where will be appended to. - /// The boolean value indicating whether the append will be performed or not. + /// The boolean value indicating whether the append operation will be performed or not. /// The value to be appended to the string builder. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendIf([ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) + // ReSharper disable once RedundantNullableFlowAttribute + public static StringBuilder AppendIf([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) { if (condition) stringBuilder.MustNotBeNull(nameof(stringBuilder)).Append(value); @@ -189,12 +198,13 @@ public static StringBuilder AppendIf([ValidatedNotNull] this StringBuilder strin /// Appends the value followed by a new line separator to the specified string builder if the condition is true. /// /// The string builder where will be appended to. - /// The boolean value indicating whether the append will be performed or not. + /// The boolean value indicating whether the append operation will be performed or not. /// The value to be appended to the string builder (optional). This value defaults to an empty string. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendLineIf([ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") + // ReSharper disable once RedundantNullableFlowAttribute + public static StringBuilder AppendLineIf([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") { if (condition) stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(value); @@ -206,7 +216,9 @@ public static StringBuilder AppendLineIf([ValidatedNotNull] this StringBuilder s /// specified . /// /// Thrown when any parameter is null. - public static StringBuilder AppendExceptionMessages([ValidatedNotNull] this StringBuilder stringBuilder, [ValidatedNotNull] Exception exception) + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendExceptionMessages([NotNull, ValidatedNotNull] this StringBuilder stringBuilder, [NotNull, ValidatedNotNull] Exception exception) + // ReSharper restore RedundantNullableFlowAttribute { stringBuilder.MustNotBeNull(nameof(stringBuilder)); exception.MustNotBeNull(nameof(exception)); @@ -228,7 +240,8 @@ public static StringBuilder AppendExceptionMessages([ValidatedNotNull] this Stri /// a single string. /// /// Thrown when is null. - public static string GetAllExceptionMessages([ValidatedNotNull] this Exception exception) => + // ReSharper disable once RedundantNullableFlowAttribute + public static string GetAllExceptionMessages([NotNull, ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); /// @@ -252,7 +265,7 @@ public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) bool wasYSuccessful; // This condition of the while loop actually has to use the single '&' operator because // y.TryAdvanceToNextNonWhiteSpaceCharacter must be called even though it already returned - // false on x. Otherwise the 'wasXSuccessful == wasYSuccessful' comparison would not return + // false on x. Otherwise, the 'wasXSuccessful == wasYSuccessful' comparison would not return // the desired result. while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) @@ -285,7 +298,7 @@ public static bool EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(this string? x, strin bool wasYSuccessful; // This condition of the while loop actually has to use the single '&' operator because // y.TryAdvanceToNextNonWhiteSpaceCharacter must be called even though it already returned - // false on x. Otherwise the 'wasXSuccessful == wasYSuccessful' comparison would not return + // false on x. Otherwise, the 'wasXSuccessful == wasYSuccessful' comparison would not return // the desired result. while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) diff --git a/Code/Light.GuardClauses/Light.GuardClauses.csproj b/Code/Light.GuardClauses/Light.GuardClauses.csproj index 6d29b41..2d7d6dc 100644 --- a/Code/Light.GuardClauses/Light.GuardClauses.csproj +++ b/Code/Light.GuardClauses/Light.GuardClauses.csproj @@ -9,7 +9,7 @@ Kenny Pflug enable true - Copyright © Kenny Pflug 2016, 2023 + Copyright © Kenny Pflug 2016, 2024 12 true true @@ -26,12 +26,12 @@ true README.md -Light.GuardClauses 11.0.0 +Light.GuardClauses 12.0.0 -------------------------------- -- Light.GuardClauses now targets .NET 8 and is AOT-compatible -- breaking: EnumInfo<T> no longer has the UnderlyingType property -- In AOT scenarios, avoid the Type-related assertions, they are marked with the DynamicallyAccessedMembersAttribute +- Added support for NotNullAttribute +- Added Range.InclusiveBetween and Range.ExclusiveBetween to create Range instances even more easily +- breaking: String assertions that use a StringComparison parameter now throw an ArgumentException instead of forwarding an invalid value to the exception factory @@ -39,7 +39,7 @@ Light.GuardClauses 11.0.0 - + diff --git a/Code/Light.GuardClauses/Range.cs b/Code/Light.GuardClauses/Range.cs index 1ecfe47..623df25 100644 --- a/Code/Light.GuardClauses/Range.cs +++ b/Code/Light.GuardClauses/Range.cs @@ -133,7 +133,7 @@ public override string ToString() => $"Range from {CreateRangeDescriptionText()}"; /// - /// Returns either "inclusive" or "exclusive", depending whether is true or false. + /// Returns either "inclusive" or "exclusive", depending on whether is true or false. /// public string LowerBoundaryText { @@ -142,7 +142,7 @@ public string LowerBoundaryText } /// - /// Returns either "inclusive" or "exclusive", depending whether is true or false. + /// Returns either "inclusive" or "exclusive", depending on whether is true or false. /// public string UpperBoundaryText { @@ -216,6 +216,26 @@ public static class Range [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Range.RangeFromInfo FromExclusive(T value) where T : IComparable => new (value, false); + /// + /// Creates a range with both boundaries inclusive. + /// + /// The lower boundary of the range. + /// The upper boundary of the range. + /// A new range with both boundaries inclusive. + /// Thrown when is less than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Range InclusiveBetween(T from, T to) where T : IComparable => new (from, to); + + /// + /// Creates a range with both boundaries exclusive. + /// + /// The lower boundary of the range. + /// The upper boundary of the range. + /// A new range with both boundaries exclusive. + /// Thrown when is less than . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Range ExclusiveBetween(T from, T to) where T : IComparable => new (from, to, false, false); + /// /// Creates a range for the specified enumerable that encompasses all valid indexes. /// @@ -288,4 +308,4 @@ public static Range For(ReadOnlyMemory memory) => [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Range For(ArraySegment segment) => new (0, segment.Count, isFromInclusive: true, isToInclusive: false); -} \ No newline at end of file +} diff --git a/Code/Version.props b/Code/Version.props index 0bedeec..1914f2f 100644 --- a/Code/Version.props +++ b/Code/Version.props @@ -1,5 +1,5 @@  - 11.0.0 + 12.0.0 \ No newline at end of file diff --git a/README.md b/README.md index fae6215..dc3ed06 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ **A lightweight .NET library for expressive Guard Clauses.** [![License](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/blob/master/LICENSE) -[![NuGet](https://img.shields.io/badge/NuGet-11.0.0-blue.svg?style=for-the-badge)](https://www.nuget.org/packages/Light.GuardClauses/) -[![Source Code](https://img.shields.io/badge/Source%20Code-11.0.0-blue.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/blob/master/Light.GuardClauses.SingleFile.cs) +[![NuGet](https://img.shields.io/badge/NuGet-12.0.0-blue.svg?style=for-the-badge)](https://www.nuget.org/packages/Light.GuardClauses/) +[![Source Code](https://img.shields.io/badge/Source%20Code-12.0.0-blue.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/blob/master/Light.GuardClauses.SingleFile.cs) [![Documentation](https://img.shields.io/badge/Docs-Wiki-yellowgreen.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/wiki) [![Documentation](https://img.shields.io/badge/Docs-Changelog-yellowgreen.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/releases) @@ -60,7 +60,7 @@ public class ConsoleWriter public void SetMovieRating(Guid movieId, int numberOfStars) { movieId.MustNotBeEmpty(); - numberOfStars.MustBeIn(Range.FromInclusive(0).ToInclusive(5)); + numberOfStars.MustBeIn(Range.InclusiveBetween(0, 5)); var movie = _movieRepo.GetById(movieId); movie.AddRating(numberOfStars); @@ -114,7 +114,7 @@ Light.GuardClauses is available as a [NuGet package](https://www.nuget.org/packa - **dotnet CLI**: `dotnet add package Light.GuardClauses` - **Visual Studio Package Manager Console**: `Install-Package Light.GuardClauses` -- **Package Reference in csproj**: `` +- **Package Reference in csproj**: `` Also, you can incorporate Light.GuardClauses as a **single source file** where the API is changed to `internal`. This is especially interesting for framework / library developers that do not want to have a dependency on the Light.GuardClauses DLL. You can grab the default .NET Standard 2.0 version in [Light.GuardClauses.SingleFile.cs](https://github.com/feO2x/Light.GuardClauses/blob/master/Light.GuardClauses.SingleFile.cs) or you can use the [Light.GuardClauses.SourceCodeTransformation](https://github.com/feO2x/Light.GuardClauses/tree/master/Code/Light.GuardClauses.SourceCodeTransformation) project to create your custom file. You can learn more about it [here](https://github.com/feO2x/Light.GuardClauses/wiki/Including-Light.GuardClauses-as-source-code).