Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/Rules/MA0050.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Sources: [ValidateArgumentsCorrectlyAnalyzer.cs](https://github.com/meziantou/Me
<!-- sources -->

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<int> Sample(string a)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,124 @@ await CreateProjectBuilder()
.ValidateAsync();
}

[Fact]
public async Task ValidValidation_ArgumentExceptionThrowIfNullOrEmpty()
{
const string SourceCode = """
using System.Collections.Generic;
class TypeName
{
IEnumerable<int> A(string a)
{
System.ArgumentException.ThrowIfNullOrEmpty(a);

return A();

IEnumerable<int> 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<int> [|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<int> [|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<int> [|A|](string a)
{
CustomArgumentException.Throw(a is null, nameof(a));
yield return 0;
}
}
""";

await CreateProjectBuilder()
.WithSourceCode(SourceCode)
.ValidateAsync();
}

[Fact]
public async Task ReportDiagnostic_IAsyncEnumerable()
{
Expand Down
Loading