diff --git a/docs/Rules/MA0050.md b/docs/Rules/MA0050.md index 9e18ed5a..d6251edb 100644 --- a/docs/Rules/MA0050.md +++ b/docs/Rules/MA0050.md @@ -4,6 +4,7 @@ Sources: [ValidateArgumentsCorrectlyAnalyzer.cs](https://github.com/meziantou/Me When a method contains a `yield` statement, the evaluation of the method is deferred until the first enumeration. This rule ensures that arguments are validated immediately, while execution of the rest of the method is still deferred. +This includes explicit `throw` statements for `ArgumentException`-derived exceptions and static `Throw*` helpers on `ArgumentException`-derived types (for example `ArgumentNullException.ThrowIfNull`). ````csharp IEnumerable Sample(string a) diff --git a/src/Meziantou.Analyzer/Rules/ValidateArgumentsCorrectlyAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ValidateArgumentsCorrectlyAnalyzer.cs index 6b68e77a..46d4160f 100755 --- a/src/Meziantou.Analyzer/Rules/ValidateArgumentsCorrectlyAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ValidateArgumentsCorrectlyAnalyzer.cs @@ -107,7 +107,7 @@ private bool IsArgumentValidation(SyntaxNodeAnalysisContext context, SyntaxNode var targetMethod = operation.TargetMethod; return targetMethod.IsStatic && targetMethod.ContainingType.IsOrInheritFrom(_argumentExceptionSymbol) && - targetMethod.Name.Contains("Throw", System.StringComparison.Ordinal); + targetMethod.Name.StartsWith("Throw", System.StringComparison.Ordinal); } } diff --git a/tests/Meziantou.Analyzer.Test/Rules/ValidateArgumentsCorrectlyAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ValidateArgumentsCorrectlyAnalyzerTests.cs index aec3a772..cf881479 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ValidateArgumentsCorrectlyAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ValidateArgumentsCorrectlyAnalyzerTests.cs @@ -221,6 +221,124 @@ await CreateProjectBuilder() .ValidateAsync(); } + [Fact] + public async Task ValidValidation_ArgumentExceptionThrowIfNullOrEmpty() + { + const string SourceCode = """ + using System.Collections.Generic; + class TypeName + { + IEnumerable A(string a) + { + System.ArgumentException.ThrowIfNullOrEmpty(a); + + return A(); + + IEnumerable A() + { + yield return 0; + if (a == null) + { + yield return 1; + } + } + } + } + """; + + await CreateProjectBuilder() + .WithTargetFramework(TargetFramework.Net8_0) + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ReportDiagnostic_ArgumentExceptionThrowIfNullOrEmpty() + { + const string SourceCode = """ + using System.Collections.Generic; + class TypeName + { + IEnumerable [|A|](string a) + { + System.ArgumentException.ThrowIfNullOrEmpty(a); + yield return 0; + } + } + """; + + await CreateProjectBuilder() + .WithTargetFramework(TargetFramework.Net8_0) + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ReportDiagnostic_CustomArgumentExceptionThrowIf() + { + const string SourceCode = """ + using System.Collections.Generic; + class CustomArgumentException : System.ArgumentException + { + public static void ThrowIf(bool condition, string paramName) + { + if (condition) + throw new CustomArgumentException(paramName); + } + + public CustomArgumentException(string paramName) : base(paramName) + { + } + } + + class TypeName + { + IEnumerable [|A|](string a) + { + CustomArgumentException.ThrowIf(a is null, nameof(a)); + yield return 0; + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ReportDiagnostic_CustomArgumentExceptionThrow() + { + const string SourceCode = """ + using System.Collections.Generic; + class CustomArgumentException : System.ArgumentException + { + public static void Throw(bool condition, string paramName) + { + if (condition) + throw new CustomArgumentException(paramName); + } + + public CustomArgumentException(string paramName) : base(paramName) + { + } + } + + class TypeName + { + IEnumerable [|A|](string a) + { + CustomArgumentException.Throw(a is null, nameof(a)); + yield return 0; + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .ValidateAsync(); + } + [Fact] public async Task ReportDiagnostic_IAsyncEnumerable() {