New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix S3415 FP/FN: Support named arguments #6759
Merged
Merged
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
c3341e2
Change implementation to use parameter names instead of position
Tim-Pohlmann 9e34b10
Improve finding of parameters
Tim-Pohlmann 656b372
Add FP test for dynamic argument
Tim-Pohlmann ead5938
Extended MethodParameterLookupBase.TryGetSyntax to work with MethodSy…
Tim-Pohlmann 59e4453
Add additional Any() check
Tim-Pohlmann b7e075e
Add more tests
Tim-Pohlmann 59f99ff
Refactor rule logic
Tim-Pohlmann de05f24
Refactor code
Tim-Pohlmann 1d07958
Minor formatting
Tim-Pohlmann e33e208
Add more tests
Tim-Pohlmann af1f519
Minor formatting
Tim-Pohlmann 163fed3
Refactoring
Tim-Pohlmann 809bcbb
More Refactoring
Tim-Pohlmann 873d748
Change issue location
Tim-Pohlmann 41c415d
Add const support
Tim-Pohlmann 31a4eee
Make MethodParameterLookupBase constructors not accept null for argum…
Tim-Pohlmann 2c35ea1
Rename test methdos for clarity
Tim-Pohlmann 6c87be7
Remove code smells and improve tests
Tim-Pohlmann d86671f
Add more tests
Tim-Pohlmann 0729173
Minor refactorings
Tim-Pohlmann 18da7c0
Simplify object creation
Tim-Pohlmann cd626a1
Add missing NotEqual support for XUnit
Tim-Pohlmann f9ea124
Improve issue location to include argument name
Tim-Pohlmann ce5108b
Fix bad calls of MethodParameterLookup constructor
Tim-Pohlmann 9dc1928
Change switch to ternary expression
Tim-Pohlmann 6ebf655
Fix error
Tim-Pohlmann fffa219
Fix formatting
Tim-Pohlmann bfeb315
Fix typo
Tim-Pohlmann File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -18,63 +18,85 @@ | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||||
*/ | ||||||
|
||||||
namespace SonarAnalyzer.Rules.CSharp | ||||||
namespace SonarAnalyzer.Rules.CSharp; | ||||||
|
||||||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||||||
public sealed class AssertionArgsShouldBePassedInCorrectOrder : SonarDiagnosticAnalyzer | ||||||
{ | ||||||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||||||
public sealed class AssertionArgsShouldBePassedInCorrectOrder : SonarDiagnosticAnalyzer | ||||||
{ | ||||||
internal const string DiagnosticId = "S3415"; | ||||||
private const string MessageFormat = "Make sure these 2 arguments are in the correct order: expected value, actual value."; | ||||||
internal const string DiagnosticId = "S3415"; | ||||||
private const string MessageFormat = "Make sure these 2 arguments are in the correct order: expected value, actual value."; | ||||||
|
||||||
private static readonly DiagnosticDescriptor Rule = DescriptorFactory.Create(DiagnosticId, MessageFormat); | ||||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); | ||||||
private static readonly DiagnosticDescriptor Rule = DescriptorFactory.Create(DiagnosticId, MessageFormat); | ||||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); | ||||||
|
||||||
private static readonly IDictionary<string, ImmutableArray<KnownType>> MethodsWithType = new Dictionary<string, ImmutableArray<KnownType>> | ||||||
protected override void Initialize(SonarAnalysisContext context) => | ||||||
context.RegisterNodeAction(c => | ||||||
{ | ||||||
["AreEqual"] = ImmutableArray.Create(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_Assert, KnownType.NUnit_Framework_Assert), | ||||||
["AreNotEqual"] = ImmutableArray.Create(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_Assert, KnownType.NUnit_Framework_Assert), | ||||||
["AreSame"] = ImmutableArray.Create(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_Assert, KnownType.NUnit_Framework_Assert), | ||||||
["AreNotSame"] = ImmutableArray.Create(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_Assert, KnownType.NUnit_Framework_Assert), | ||||||
["Equal"] = ImmutableArray.Create(KnownType.Xunit_Assert), | ||||||
["Same"] = ImmutableArray.Create(KnownType.Xunit_Assert), | ||||||
["NotSame"] = ImmutableArray.Create(KnownType.Xunit_Assert) | ||||||
}; | ||||||
|
||||||
protected override void Initialize(SonarAnalysisContext context) => | ||||||
context.RegisterNodeAction(c => | ||||||
if (c.Node is InvocationExpressionSyntax { ArgumentList: { Arguments.Count: >= 2 } argumentList } invocation | ||||||
&& GetParameters(invocation.GetName()) is { } knownAssertParameters | ||||||
&& c.SemanticModel.GetSymbolInfo(invocation).AllSymbols() | ||||||
.SelectMany(symbol => | ||||||
symbol is IMethodSymbol { IsStatic: true, ContainingSymbol: INamedTypeSymbol container } methodSymbol | ||||||
? knownAssertParameters.Select(knownParameters => FindWrongArguments(c.SemanticModel, container, methodSymbol, argumentList, knownParameters)) | ||||||
: Enumerable.Empty<WrongArguments?>()) | ||||||
.FirstOrDefault(x => x is not null) is (Expected: var expected, Actual: var actual)) | ||||||
{ | ||||||
var methodCall = (InvocationExpressionSyntax)c.Node; | ||||||
if (!methodCall.Expression.IsKind(SyntaxKind.SimpleMemberAccessExpression) | ||||||
|| methodCall.ArgumentList.Arguments.Count < 2) | ||||||
{ | ||||||
return; | ||||||
} | ||||||
c.ReportIssue(Diagnostic.Create(Rule, CreateLocation(expected, actual))); | ||||||
} | ||||||
}, | ||||||
SyntaxKind.InvocationExpression); | ||||||
|
||||||
var firstArgument = methodCall.ArgumentList.Arguments[0]; | ||||||
var secondArgument = methodCall.ArgumentList.Arguments[1]; | ||||||
if (firstArgument.Expression is LiteralExpressionSyntax | ||||||
|| secondArgument.Expression is not LiteralExpressionSyntax) | ||||||
private static KnownAssertParameters[] GetParameters(string name) => | ||||||
name switch | ||||||
{ | ||||||
"AreEqual" => new KnownAssertParameters[] | ||||||
{ | ||||||
return; | ||||||
} | ||||||
|
||||||
var methodCallExpression = (MemberAccessExpressionSyntax)methodCall.Expression; | ||||||
|
||||||
var methodKnownTypes = MethodsWithType.GetValueOrDefault(methodCallExpression.Name.Identifier.ValueText); | ||||||
if (methodKnownTypes == null) | ||||||
new(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_Assert, "expected", "actual"), | ||||||
new(KnownType.NUnit_Framework_Assert, "expected", "actual") | ||||||
}, | ||||||
"AreNotEqual" => new KnownAssertParameters[] | ||||||
{ | ||||||
return; | ||||||
} | ||||||
|
||||||
var symbolInfo = c.SemanticModel.GetSymbolInfo(methodCallExpression.Expression).Symbol; | ||||||
var isAnyTrackedAssertType = (symbolInfo as INamedTypeSymbol).IsAny(methodKnownTypes); | ||||||
if (!isAnyTrackedAssertType) | ||||||
new(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_Assert, "notExpected", "actual"), | ||||||
new(KnownType.NUnit_Framework_Assert, "expected", "actual") | ||||||
}, | ||||||
"AreSame" => new KnownAssertParameters[] | ||||||
{ | ||||||
new(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_Assert, "expected", "actual"), | ||||||
new(KnownType.NUnit_Framework_Assert, "expected", "actual") | ||||||
}, | ||||||
"AreNotSame" => new KnownAssertParameters[] | ||||||
{ | ||||||
new(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_Assert, "notExpected", "actual"), | ||||||
new(KnownType.NUnit_Framework_Assert, "expected", "actual") | ||||||
}, | ||||||
"Equal" or "NotEqual" or "Same" or "NotSame" => new KnownAssertParameters[] | ||||||
{ | ||||||
return; | ||||||
} | ||||||
new(KnownType.Xunit_Assert, "expected", "actual") | ||||||
}, | ||||||
_ => null | ||||||
}; | ||||||
|
||||||
private static WrongArguments? FindWrongArguments(SemanticModel semanticModel, | ||||||
INamedTypeSymbol container, | ||||||
IMethodSymbol symbol, | ||||||
ArgumentListSyntax argumentList, | ||||||
KnownAssertParameters knownParameters) => | ||||||
container.Is(knownParameters.AssertClass) | ||||||
&& CSharpFacade.Instance.MethodParameterLookup(argumentList, symbol) is var parameterLookup | ||||||
&& parameterLookup.TryGetSyntax(knownParameters.ExpectedParamterName, out var expectedArguments) | ||||||
&& expectedArguments.FirstOrDefault() is { } expected | ||||||
&& semanticModel.GetConstantValue(expected).HasValue is false | ||||||
&& parameterLookup.TryGetSyntax(knownParameters.ActualParameterName, out var actualArguments) | ||||||
&& actualArguments.FirstOrDefault() is { } actual | ||||||
&& semanticModel.GetConstantValue(actual).HasValue is true | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
? new(expected, actual) | ||||||
: null; | ||||||
|
||||||
private static Location CreateLocation(SyntaxNode argument1, SyntaxNode argument2) => | ||||||
argument1.Span.CompareTo(argument2.Span) < 0 | ||||||
? argument1.Parent.CreateLocation(argument2.Parent) | ||||||
: argument2.Parent.CreateLocation(argument1.Parent); | ||||||
|
||||||
c.ReportIssue(Diagnostic.Create(Rule, firstArgument.CreateLocation(secondArgument))); | ||||||
}, | ||||||
SyntaxKind.InvocationExpression); | ||||||
} | ||||||
private readonly record struct KnownAssertParameters(KnownType AssertClass, string ExpectedParamterName, string ActualParameterName); | ||||||
private readonly record struct WrongArguments(SyntaxNode Expected, SyntaxNode Actual); | ||||||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo