diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f45972fde..45b218c9f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -56,6 +56,7 @@ jobs:
- run: dotnet build src/Meziantou.Analyzer/Meziantou.Analyzer.csproj --configuration Release /p:RoslynVersion=roslyn3.8 /p:Version=${{ needs.compute_package_version.outputs.package_version }}
- run: dotnet build src/Meziantou.Analyzer/Meziantou.Analyzer.csproj --configuration Release /p:RoslynVersion=roslyn4.2 /p:Version=${{ needs.compute_package_version.outputs.package_version }}
- run: dotnet build src/Meziantou.Analyzer/Meziantou.Analyzer.csproj --configuration Release /p:RoslynVersion=roslyn4.4 /p:Version=${{ needs.compute_package_version.outputs.package_version }}
+ - run: dotnet build src/Meziantou.Analyzer/Meziantou.Analyzer.csproj --configuration Release /p:RoslynVersion=roslyn4.6 /p:Version=${{ needs.compute_package_version.outputs.package_version }}
- run: dotnet restore src/Meziantou.Analyzer.pack.csproj
- run: dotnet pack src/Meziantou.Analyzer.pack.csproj --configuration Release --no-build /p:Version=${{ needs.compute_package_version.outputs.package_version }}
diff --git a/Directory.Build.props b/Directory.Build.props
index 59d5e028e..ba9b04377 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -39,7 +39,7 @@
all
runtime; build; native; contentfiles; analyzers
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 496df64db..0c6ebf26d 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -42,6 +42,18 @@
+
+
+
+
+
+
+ $(DefineConstants);ROSLYN_4_6;ROSLYN_4_2_OR_GREATER;ROSLYN_4_4_OR_GREATER;ROSLYN_4_6_OR_GREATER
+ $(DefineConstants);CSHARP9_OR_GREATER;CSHARP10_OR_GREATER;CSHARP11_OR_GREATER
+ $(NoWarn);nullable
+
+
+
@@ -49,7 +61,7 @@
$(DefineConstants);ROSLYN4_4;ROSLYN_4_2_OR_GREATER;ROSLYN_4_4_OR_GREATER;ROSLYN_4_5_OR_GREATER;ROSLYN_4_6_OR_GREATER
- $(DefineConstants);CSHARP9_OR_GREATER;CSHARP10_OR_GREATER;CSHARP11_OR_GREATER
+ $(DefineConstants);CSHARP9_OR_GREATER;CSHARP10_OR_GREATER;CSHARP11_OR_GREATER;CSHARP12_OR_GREATER
$(NoWarn);CS0618
diff --git a/src/Meziantou.Analyzer.pack.csproj b/src/Meziantou.Analyzer.pack.csproj
index 36dd02b59..f9fcda286 100644
--- a/src/Meziantou.Analyzer.pack.csproj
+++ b/src/Meziantou.Analyzer.pack.csproj
@@ -19,5 +19,6 @@
+
\ No newline at end of file
diff --git a/src/Meziantou.Analyzer/Internals/OperationExtensions.cs b/src/Meziantou.Analyzer/Internals/OperationExtensions.cs
index d18295d1e..a116101bc 100644
--- a/src/Meziantou.Analyzer/Internals/OperationExtensions.cs
+++ b/src/Meziantou.Analyzer/Internals/OperationExtensions.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
@@ -98,7 +99,7 @@ public static IOperation UnwrapImplicitConversionOperations(this IOperation oper
return operation;
}
-
+
public static IOperation UnwrapConversionOperations(this IOperation operation)
{
if (operation is IConversionOperation conversionOperation)
@@ -133,4 +134,111 @@ public static bool HasArgumentOfType(this IInvocationOperation operation, ITypeS
return null;
}
+
+ public static bool IsInStaticContext(this IOperation operation, CancellationToken cancellationToken) => IsInStaticContext(operation, cancellationToken, out _);
+ public static bool IsInStaticContext(this IOperation operation, CancellationToken cancellationToken, out int parentStaticMemberStartPosition)
+ {
+ // Local functions can be nested, and an instance local function can be declared
+ // in a static local function. So, you need to continue to check ancestors when a
+ // local function is not static.
+ foreach (var member in operation.Syntax.Ancestors())
+ {
+ if (member is LocalFunctionStatementSyntax localFunction)
+ {
+ var symbol = operation.SemanticModel!.GetDeclaredSymbol(localFunction, cancellationToken);
+ if (symbol != null && symbol.IsStatic)
+ {
+ parentStaticMemberStartPosition = localFunction.GetLocation().SourceSpan.Start;
+ return true;
+ }
+ }
+ else if (member is LambdaExpressionSyntax lambdaExpression)
+ {
+ var symbol = operation.SemanticModel!.GetSymbolInfo(lambdaExpression, cancellationToken).Symbol;
+ if (symbol != null && symbol.IsStatic)
+ {
+ parentStaticMemberStartPosition = lambdaExpression.GetLocation().SourceSpan.Start;
+ return true;
+ }
+ }
+ else if (member is AnonymousMethodExpressionSyntax anonymousMethod)
+ {
+ var symbol = operation.SemanticModel!.GetSymbolInfo(anonymousMethod, cancellationToken).Symbol;
+ if (symbol != null && symbol.IsStatic)
+ {
+ parentStaticMemberStartPosition = anonymousMethod.GetLocation().SourceSpan.Start;
+ return true;
+ }
+ }
+ else if (member is MethodDeclarationSyntax methodDeclaration)
+ {
+ parentStaticMemberStartPosition = methodDeclaration.GetLocation().SourceSpan.Start;
+
+ var symbol = operation.SemanticModel!.GetDeclaredSymbol(methodDeclaration, cancellationToken);
+ return symbol != null && symbol.IsStatic;
+ }
+ }
+
+ parentStaticMemberStartPosition = -1;
+ return false;
+ }
+
+ public static IEnumerable LookupAvailableSymbols(this IOperation operation, CancellationToken cancellationToken)
+ {
+ // Find available symbols
+ var operationLocation = operation.Syntax.GetLocation().SourceSpan.Start;
+ var isInStaticContext = operation.IsInStaticContext(cancellationToken, out var parentStaticMemberStartPosition);
+ foreach (var symbol in operation.SemanticModel!.LookupSymbols(operationLocation))
+ {
+ // LookupSymbols check the accessibility of the symbol, but it can
+ // suggest instance members when the current context is static.
+ if (symbol is IFieldSymbol field && isInStaticContext && !field.IsStatic)
+ continue;
+
+ if (symbol is IPropertySymbol { GetMethod: not null } property && isInStaticContext && !property.IsStatic)
+ continue;
+
+ // Locals can be returned even if there are not valid in the current context. For instance,
+ // it can return locals declared after the current location. Or it can return locals that
+ // should not be accessible in a static local function.
+ //
+ // void Sample()
+ // {
+ // int local = 0;
+ // static void LocalFunction() => local; <-- local is invalid here but LookupSymbols suggests it
+ // }
+ //
+ // Parameters from the ancestor methods are also returned even if the operation is in a static local function.
+ if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter)
+ {
+ var isValid = true;
+ foreach (var location in symbol.Locations)
+ {
+ isValid &= IsValid(location, operationLocation, isInStaticContext ? parentStaticMemberStartPosition : null);
+ if (!isValid)
+ break;
+ }
+
+ if (!isValid)
+ continue;
+
+ static bool IsValid(Location location, int operationLocation, int? staticContextStart)
+ {
+ var localPosition = location.SourceSpan.Start;
+
+ // The local is declared after the current expression
+ if (localPosition > operationLocation)
+ return false;
+
+ // The local is declared outside the static local function
+ if (staticContextStart.HasValue && localPosition < staticContextStart.GetValueOrDefault())
+ return false;
+
+ return true;
+ }
+ }
+
+ yield return symbol;
+ }
+ }
}
diff --git a/src/Meziantou.Analyzer/Internals/SymbolExtensions.cs b/src/Meziantou.Analyzer/Internals/SymbolExtensions.cs
index 5d054acc6..bc4f80aea 100644
--- a/src/Meziantou.Analyzer/Internals/SymbolExtensions.cs
+++ b/src/Meziantou.Analyzer/Internals/SymbolExtensions.cs
@@ -132,4 +132,16 @@ public static bool IsTopLevelStatementsEntryPointType([NotNullWhen(true)] this I
return false;
}
+
+ public static ITypeSymbol? GetSymbolType(this ISymbol symbol)
+ {
+ return symbol switch
+ {
+ IParameterSymbol parameter => parameter.Type,
+ IFieldSymbol field => field.Type,
+ IPropertySymbol { GetMethod: not null } property => property.Type,
+ ILocalSymbol local => local.Type,
+ _ => null,
+ };
+ }
}
diff --git a/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs
index 658e5fb6c..b94203777 100644
--- a/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs
+++ b/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs
@@ -120,128 +120,15 @@ private static void Analyze(OperationAnalysisContext context)
private static IEnumerable GetParameterNames(IOperation operation, CancellationToken cancellationToken)
{
- var semanticModel = operation.SemanticModel!;
- var node = operation.Syntax;
- while (node != null)
+ var symbols = operation.LookupAvailableSymbols(cancellationToken);
+ foreach (var symbol in symbols)
{
- switch (node)
+ switch (symbol)
{
- case AccessorDeclarationSyntax accessor:
- if (accessor.IsKind(SyntaxKind.SetAccessorDeclaration))
- {
- yield return "value";
- }
-
+ case IParameterSymbol parameterSymbol:
+ yield return parameterSymbol.Name;
break;
-
- case PropertyDeclarationSyntax _:
- yield break;
-
- case IndexerDeclarationSyntax indexerDeclarationSyntax:
- {
- var symbol = semanticModel.GetDeclaredSymbol(indexerDeclarationSyntax, cancellationToken);
- if (symbol != null)
- {
- foreach (var parameter in symbol.Parameters)
- yield return parameter.Name;
- }
-
- yield break;
- }
-
- case MethodDeclarationSyntax methodDeclaration:
- {
- var symbol = semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken);
- if (symbol != null)
- {
- foreach (var parameter in symbol.Parameters)
- yield return parameter.Name;
- }
-
- yield break;
- }
-
- case LocalFunctionStatementSyntax localFunctionStatement:
- {
- if (semanticModel.GetDeclaredSymbol(localFunctionStatement, cancellationToken) is IMethodSymbol symbol)
- {
- foreach (var parameter in symbol.Parameters)
- yield return parameter.Name;
- }
-
- break;
- }
-
- case ConstructorDeclarationSyntax constructorDeclaration:
- {
- var symbol = semanticModel.GetDeclaredSymbol(constructorDeclaration, cancellationToken);
- if (symbol != null)
- {
- foreach (var parameter in symbol.Parameters)
- yield return parameter.Name;
- }
-
- yield break;
- }
-
- case OperatorDeclarationSyntax operatorDeclaration:
- {
- var symbol = semanticModel.GetDeclaredSymbol(operatorDeclaration, cancellationToken);
- if (symbol != null)
- {
- foreach (var parameter in symbol.Parameters)
- yield return parameter.Name;
- }
-
- yield break;
- }
-
- case ParenthesizedLambdaExpressionSyntax parenthesizedLambdaExpressionSyntax:
- {
- foreach (var parameter in parenthesizedLambdaExpressionSyntax.ParameterList.Parameters)
- {
- if (!string.IsNullOrEmpty(parameter.Identifier.ValueText))
- yield return parameter.Identifier.ValueText;
- }
-
- if (parenthesizedLambdaExpressionSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)))
- yield break;
-
- break;
- }
-
- case SimpleLambdaExpressionSyntax lambdaExpressionSyntax:
- {
- if (!string.IsNullOrEmpty(lambdaExpressionSyntax.Parameter?.Identifier.ValueText))
- {
- yield return lambdaExpressionSyntax.Parameter.Identifier.ValueText;
- }
-
- if (lambdaExpressionSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)))
- yield break;
-
- break;
- }
-
- case AnonymousMethodExpressionSyntax anonymousMethodExpressionSyntax:
- {
- if (anonymousMethodExpressionSyntax.ParameterList != null)
- {
- foreach (var parameter in anonymousMethodExpressionSyntax.ParameterList.Parameters)
- {
- if (!string.IsNullOrEmpty(parameter.Identifier.ValueText))
- yield return parameter.Identifier.ValueText;
- }
- }
-
- if (anonymousMethodExpressionSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)))
- yield break;
-
- break;
- }
}
-
- node = node.Parent;
}
}
}
diff --git a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs
index a7f7c7e31..b1de52e67 100644
--- a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs
+++ b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs
@@ -308,57 +308,21 @@ public void AnalyzeLoop(OperationAnalysisContext context)
private string[] FindCancellationTokens(IOperation operation, CancellationToken cancellationToken)
{
- // Find available symbols
- var operationLocation = operation.Syntax.GetLocation().SourceSpan.Start;
- var isInStaticContext = IsInStaticContext(operation, cancellationToken, out var parentStaticMemberStartPosition);
var availableSymbols = new List();
- foreach (var symbol in operation.SemanticModel!.LookupSymbols(operationLocation))
+ foreach (var symbol in operation.LookupAvailableSymbols(cancellationToken))
{
- // LookupSymbols check the accessibility of the symbol, but it can
- // suggest instance members when the current context is static.
- var symbolType = symbol switch
- {
- IParameterSymbol parameter => parameter.Type,
- IFieldSymbol field when !isInStaticContext || field.IsStatic => field.Type,
- IPropertySymbol { GetMethod: not null } property when !isInStaticContext || property.IsStatic => property.Type,
- ILocalSymbol local => local.Type,
- _ => null,
- };
-
+ var symbolType = symbol.GetSymbolType();
if (symbolType == null)
continue;
- // Locals can be returned even if there are not valid in the current context. For instance,
- // it can return locals declared after the current location. Or it can return locals that
- // should not be accessible in a static local function.
- //
- // void Sample()
- // {
- // int local = 0;
- // static void LocalFunction() => local; <-- local is invalid here but LookupSymbols suggests it
- // }
- //
- // Parameters from the ancestor methods are also returned even if the operation is in a static local function.
- if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter)
- {
- var localPosition = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(cancellationToken).GetLocation().SourceSpan.Start;
-
- // The local is not part of the source tree
- if (localPosition == null)
- continue;
-
- // The local is declared after the current expression
- if (localPosition > operationLocation)
- continue;
-
- // The local is declared outside the static local function
- if (isInStaticContext && localPosition < parentStaticMemberStartPosition)
- continue;
- }
-
availableSymbols.Add(new(symbol.Name, symbolType));
}
+ if (availableSymbols.Count == 0)
+ return Array.Empty();
+
+ var isInStaticContext = operation.IsInStaticContext(cancellationToken);
+
// For each symbol, get their members
var paths = new List();
foreach (var availableSymbol in availableSymbols)
@@ -426,53 +390,6 @@ static bool IsSymbolAccessibleFromOperation(ISymbol symbol, IOperation operation
return operation.SemanticModel!.GetDeclaredSymbol(ancestor, cancellationToken) as ITypeSymbol;
}
- private static bool IsInStaticContext(IOperation operation, CancellationToken cancellationToken, out int parentStaticMemberStartPosition)
- {
- // Local functions can be nested, and an instance local function can be declared
- // in a static local function. So, you need to continue to check ancestors when a
- // local function is not static.
- foreach (var member in operation.Syntax.Ancestors())
- {
- if (member is LocalFunctionStatementSyntax localFunction)
- {
- var symbol = operation.SemanticModel!.GetDeclaredSymbol(localFunction, cancellationToken);
- if (symbol != null && symbol.IsStatic)
- {
- parentStaticMemberStartPosition = localFunction.GetLocation().SourceSpan.Start;
- return true;
- }
- }
- else if (member is LambdaExpressionSyntax lambdaExpression)
- {
- var symbol = operation.SemanticModel!.GetSymbolInfo(lambdaExpression, cancellationToken).Symbol;
- if (symbol != null && symbol.IsStatic)
- {
- parentStaticMemberStartPosition = lambdaExpression.GetLocation().SourceSpan.Start;
- return true;
- }
- }
- else if (member is AnonymousMethodExpressionSyntax anonymousMethod)
- {
- var symbol = operation.SemanticModel!.GetSymbolInfo(anonymousMethod, cancellationToken).Symbol;
- if (symbol != null && symbol.IsStatic)
- {
- parentStaticMemberStartPosition = anonymousMethod.GetLocation().SourceSpan.Start;
- return true;
- }
- }
- else if (member is MethodDeclarationSyntax methodDeclaration)
- {
- parentStaticMemberStartPosition = methodDeclaration.GetLocation().SourceSpan.Start;
-
- var symbol = operation.SemanticModel!.GetDeclaredSymbol(methodDeclaration, cancellationToken);
- return symbol != null && symbol.IsStatic;
- }
- }
-
- parentStaticMemberStartPosition = -1;
- return false;
- }
-
private static IEnumerable Prepend(T value, IEnumerable items)
{
yield return value;
diff --git a/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs
index efc77117e..1bdc1f36d 100644
--- a/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs
+++ b/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs
@@ -85,7 +85,7 @@ bool IsDateTimeOffsetUnixEpoch()
}
else if (operation.Arguments.Length == 2)
{
- if (ArgumentsEquals(operation.Arguments.AsSpan().Slice(0, 1), new object[] { 621355968000000000L }) && IsTimeSpanZero(operation.Arguments[1]))
+ if (ArgumentsEquals(operation.Arguments.AsSpan(0, 1), new object[] { 621355968000000000L }) && IsTimeSpanZero(operation.Arguments[1]))
return true;
if (IsUnixEpochProperty(operation.Arguments[0]) && IsTimeSpanZero(operation.Arguments[1]))
@@ -93,17 +93,17 @@ bool IsDateTimeOffsetUnixEpoch()
}
else if (operation.Arguments.Length == 7)
{
- if (ArgumentsEquals(operation.Arguments.AsSpan().Slice(0, 6), new object[] { 1970, 1, 1, 0, 0, 0 }) && IsTimeSpanZero(operation.Arguments[6]))
+ if (ArgumentsEquals(operation.Arguments.AsSpan(0, 6), new object[] { 1970, 1, 1, 0, 0, 0 }) && IsTimeSpanZero(operation.Arguments[6]))
return true;
}
else if (operation.Arguments.Length == 8)
{
- if (ArgumentsEquals(operation.Arguments.AsSpan().Slice(0, 7), new object[] { 1970, 1, 1, 0, 0, 0, 0 }) && IsTimeSpanZero(operation.Arguments[7]))
+ if (ArgumentsEquals(operation.Arguments.AsSpan(0, 7), new object[] { 1970, 1, 1, 0, 0, 0, 0 }) && IsTimeSpanZero(operation.Arguments[7]))
return true;
}
else if (operation.Arguments.Length == 9)
{
- if (ArgumentsEquals(operation.Arguments.AsSpan().Slice(0, 8), new object[] { 1970, 1, 1, 0, 0, 0, 0, 0 }) && IsTimeSpanZero(operation.Arguments[8]))
+ if (ArgumentsEquals(operation.Arguments.AsSpan(0, 8), new object[] { 1970, 1, 1, 0, 0, 0, 0, 0 }) && IsTimeSpanZero(operation.Arguments[8]))
return true;
}
@@ -134,7 +134,7 @@ private static bool IsDateTimeUnixEpoch(IObjectCreationOperation operation, Comp
}
else if (operation.Arguments.Length == 2)
{
- if (ArgumentsEquals(operation.Arguments.AsSpan().Slice(0, 1), new object[] { 621355968000000000L }) && IsDateTimeKindUtc(compilation, operation.Arguments[1]))
+ if (ArgumentsEquals(operation.Arguments.AsSpan(0, 1), new object[] { 621355968000000000L }) && IsDateTimeKindUtc(compilation, operation.Arguments[1]))
return true;
}
else if (operation.Arguments.Length == 3)
@@ -149,7 +149,7 @@ private static bool IsDateTimeUnixEpoch(IObjectCreationOperation operation, Comp
}
else if (operation.Arguments.Length == 7)
{
- if (ArgumentsEquals(operation.Arguments.AsSpan().Slice(0, 6), new object[] { 1970, 1, 1, 0, 0, 0 }) && IsDateTimeKindUtc(compilation, operation.Arguments[6]))
+ if (ArgumentsEquals(operation.Arguments.AsSpan(0, 6), new object[] { 1970, 1, 1, 0, 0, 0 }) && IsDateTimeKindUtc(compilation, operation.Arguments[6]))
return true;
}
diff --git a/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs
index 829499cea..a76e52fba 100644
--- a/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs
+++ b/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs
@@ -14,49 +14,134 @@ private static ProjectBuilder CreateProjectBuilder()
}
[Fact]
- public async Task ArgumentNameIsSpecified_ShouldNotReportError()
+ public async Task ArgumentNameIsSpecified_Record_ShouldNotReportError()
{
- var sourceCode = @"
-class Sample
-{
- string Prop
+ var sourceCode = """
+ using System;
+ internal sealed record ManuscriptId(int Id)
+ {
+ public int Id { get; } = Id > 0 ? Id : throw new ArgumentOutOfRangeException(paramName: nameof(Id), Id, message: "Must be greater than 0");
+ }
+ """;
+
+ await CreateProjectBuilder()
+ .WithSourceCode(sourceCode)
+ .ValidateAsync();
+ }
+
+ [Fact]
+ public async Task ArgumentNameIsSpecified_LocalFunction_ShouldNotReportError()
{
- get { throw null; }
- set { throw new System.ArgumentNullException(nameof(value)); }
+ var sourceCode = """
+ class Sample
+ {
+ void Test(string test)
+ {
+ void LocalFunction(string a)
+ {
+ throw new System.ArgumentNullException(nameof(a));
+ }
+ }
+ }
+ """;
+
+ await CreateProjectBuilder()
+ .WithSourceCode(sourceCode)
+ .ValidateAsync();
}
- string this[int index]
+ [Fact]
+ public async Task ArgumentNameIsSpecified_LocalFunction_ArgumentFromParentMethod_ShouldNotReportError()
{
- get { throw new System.ArgumentNullException(nameof(index)); }
- set { throw new System.ArgumentNullException(nameof(index)); }
+ var sourceCode = """
+ class Sample
+ {
+ void Test(string test)
+ {
+ void LocalFunction()
+ {
+ throw new System.ArgumentNullException(nameof(test));
+ }
+ }
+ }
+ """;
+
+ await CreateProjectBuilder()
+ .WithSourceCode(sourceCode)
+ .ValidateAsync();
}
- Sample(string test)
+ [Fact]
+ public async Task ArgumentNameIsSpecified_Operator_ShouldNotReportError()
{
- throw new System.Exception();
- throw new System.ArgumentException(""message"", nameof(test));
- throw new System.ArgumentNullException(nameof(test));
+ var sourceCode = """
+ class Sample
+ {
+ public static Sample operator +(Sample first, Sample second)
+ {
+ throw new System.ArgumentNullException(nameof(first));
+ throw new System.ArgumentNullException(nameof(second));
+ }
+ }
+ """;
+
+ await CreateProjectBuilder()
+ .WithSourceCode(sourceCode)
+ .ValidateAsync();
}
- void Test(string test)
+ [Fact]
+ public async Task ArgumentNameIsSpecified_Method_ShouldNotReportError()
{
- throw new System.Exception();
- throw new System.ArgumentException(""message"", nameof(test));
- throw new System.ArgumentNullException(nameof(test));
- throw new System.ComponentModel.InvalidEnumArgumentException(nameof(test), 0, typeof(System.Enum));
+ var sourceCode = """
+ class Sample
+ {
+ Sample(string test)
+ {
+ throw new System.Exception();
+ throw new System.ArgumentException("message", nameof(test));
+ throw new System.ArgumentNullException(nameof(test));
+ }
+ }
+ """;
- void LocalFunction(string a)
- {
- throw new System.ArgumentNullException(nameof(a));
- }
+ await CreateProjectBuilder()
+ .WithSourceCode(sourceCode)
+ .ValidateAsync();
}
- public static Sample operator +(Sample first, Sample second)
+ [Fact]
+ public async Task ArgumentNameIsSpecified_Indexer_ShouldNotReportError()
{
- throw new System.ArgumentNullException(nameof(first));
- throw new System.ArgumentNullException(nameof(second));
+ var sourceCode = """
+ class Sample
+ {
+ string this[int index]
+ {
+ get { throw new System.ArgumentNullException(nameof(index)); }
+ set { throw new System.ArgumentNullException(nameof(index)); }
+ }
+ }
+ """;
+
+ await CreateProjectBuilder()
+ .WithSourceCode(sourceCode)
+ .ValidateAsync();
}
-}";
+
+ [Fact]
+ public async Task ArgumentNameIsSpecified_Setter_ShouldNotReportError()
+ {
+ var sourceCode = """
+ class Sample
+ {
+ string Prop
+ {
+ get { throw null; }
+ set { throw new System.ArgumentNullException(nameof(value)); }
+ }
+ }
+ """;
await CreateProjectBuilder()
.WithSourceCode(sourceCode)
@@ -327,4 +412,23 @@ await CreateProjectBuilder()
.WithOutputKind(Microsoft.CodeAnalysis.OutputKind.ConsoleApplication)
.ValidateAsync();
}
+
+#if CSHARP12_OR_GREATER
+ [Fact]
+ public async Task PrimaryConstructor()
+ {
+ const string SourceCode = """"
+ using System;
+
+ public class Sample(string id)
+ {
+ void A() => throw new ArgumentException("", nameof(id));
+ }
+ """";
+ await CreateProjectBuilder()
+ .WithSourceCode(SourceCode)
+ .WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.Preview)
+ .ValidateAsync();
+ }
+#endif
}