Skip to content
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 S4144 FP: when type constraints are used #9398

Merged
merged 33 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e4141f1
Fix via semantic model type check
martin-strecker-sonarsource Jun 4, 2024
3fc4b3e
Refactor
martin-strecker-sonarsource Jun 4, 2024
baf9a5f
Fix
martin-strecker-sonarsource Jun 4, 2024
3b3d68f
Unsued using
martin-strecker-sonarsource Jun 4, 2024
0170497
Add proper support for type parameters and add more tests
martin-strecker-sonarsource Jun 5, 2024
798bb24
Better comments
martin-strecker-sonarsource Jun 5, 2024
f7cc661
Test improvements
martin-strecker-sonarsource Jun 5, 2024
08d5da4
fix test
martin-strecker-sonarsource Jun 5, 2024
fccc74d
Make CombinatorialDataAttribute derive from TestMethodAttribute
martin-strecker-sonarsource Jun 5, 2024
c378171
wip: Fix..
martin-strecker-sonarsource Jun 6, 2024
de61440
Rename to CombinatorialDataTestMethod
martin-strecker-sonarsource Jun 7, 2024
8605bd4
Remove unsed parameter
martin-strecker-sonarsource Jun 7, 2024
a45e584
Add support for VB
martin-strecker-sonarsource Jun 7, 2024
8f70d1a
Add VB tests
martin-strecker-sonarsource Jun 7, 2024
16bcfde
More VB tests
martin-strecker-sonarsource Jun 7, 2024
093915b
Correct tests
martin-strecker-sonarsource Jun 7, 2024
586d42b
Net only test
martin-strecker-sonarsource Jun 10, 2024
a26c3ce
Fix code smells and formatting
martin-strecker-sonarsource Jun 10, 2024
3c35dac
Add all combinations for constraints
martin-strecker-sonarsource Jun 10, 2024
7a4d933
Review
martin-strecker-sonarsource Jun 12, 2024
01944a9
Move test cases and add a further nested example
martin-strecker-sonarsource Jun 12, 2024
7fc57b4
Codecoverage: More tests
martin-strecker-sonarsource Jun 12, 2024
50c9dde
Simplify
martin-strecker-sonarsource Jun 12, 2024
1ed56ed
Formatting
martin-strecker-sonarsource Jun 12, 2024
8f9ffc3
Grammar
martin-strecker-sonarsource Jun 12, 2024
6a3504d
Better is ITypeParameterSymbol check
martin-strecker-sonarsource Jun 12, 2024
90d582b
Better names
martin-strecker-sonarsource Jun 12, 2024
b9e7640
Simplify
martin-strecker-sonarsource Jun 13, 2024
f17f3b3
Add OriginalDefinition check and test
martin-strecker-sonarsource Jun 13, 2024
18aab61
Better comment
martin-strecker-sonarsource Jun 13, 2024
0288dd8
Correct comment
martin-strecker-sonarsource Jun 13, 2024
2cdf1b3
Fix name
martin-strecker-sonarsource Jun 19, 2024
9b25eb7
Comment
martin-strecker-sonarsource Jun 20, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ protected override IEnumerable<IMethodDeclaration> GetMethodDeclarations(SyntaxN
? ((CompilationUnitSyntax)node).GetMethodDeclarations()
: ((TypeDeclarationSyntax)node).GetMethodDeclarations();

protected override bool AreDuplicates(IMethodDeclaration firstMethod, IMethodDeclaration secondMethod) =>
protected override bool AreDuplicates(SemanticModel model, IMethodDeclaration firstMethod, IMethodDeclaration secondMethod) =>
firstMethod is { Body: { Statements: { Count: > 1 } } }
&& firstMethod.Identifier.ValueText != secondMethod.Identifier.ValueText
&& HaveSameParameters<ParameterSyntax>(firstMethod.ParameterList.Parameters, secondMethod.ParameterList.Parameters)
&& HaveSameParameters<ParameterSyntax>(model, firstMethod.ParameterList.Parameters, secondMethod.ParameterList.Parameters)
&& firstMethod.Body.IsEquivalentTo(secondMethod.Body, false);

protected override SyntaxToken GetMethodIdentifier(IMethodDeclaration method) =>
Expand Down
Tim-Pohlmann marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public abstract class MethodsShouldNotHaveIdenticalImplementationsBase<TSyntaxKi

protected abstract IEnumerable<TMethodDeclarationSyntax> GetMethodDeclarations(SyntaxNode node);
protected abstract SyntaxToken GetMethodIdentifier(TMethodDeclarationSyntax method);
protected abstract bool AreDuplicates(TMethodDeclarationSyntax firstMethod, TMethodDeclarationSyntax secondMethod);
protected abstract bool AreDuplicates(SemanticModel model, TMethodDeclarationSyntax firstMethod, TMethodDeclarationSyntax secondMethod);

protected override string MessageFormat => "Update this method so that its implementation is not identical to '{0}'.";

Expand All @@ -50,7 +50,7 @@ protected override void Initialize(SonarAnalysisContext context) =>
{
var method = methodsToHandle.First.Value;
methodsToHandle.RemoveFirst();
var duplicates = methodsToHandle.Where(x => AreDuplicates(method, x)).ToList();
var duplicates = methodsToHandle.Where(x => AreDuplicates(c.SemanticModel, method, x)).ToList();

foreach (var duplicate in duplicates)
{
Expand All @@ -64,12 +64,23 @@ protected override void Initialize(SonarAnalysisContext context) =>
protected virtual bool IsExcludedFromBeingExamined(SonarSyntaxNodeReportingContext context) =>
context.ContainingSymbol.Kind != SymbolKind.NamedType;

protected static bool HaveSameParameters<TSyntax>(SeparatedSyntaxList<TSyntax>? leftParameters, SeparatedSyntaxList<TSyntax>? rightParameters)
protected static bool HaveSameParameters<TSyntax>(SemanticModel model, SeparatedSyntaxList<TSyntax>? leftParameters, SeparatedSyntaxList<TSyntax>? rightParameters)
where TSyntax : SyntaxNode =>
(leftParameters == null && rightParameters == null)
|| (leftParameters != null
&& rightParameters != null
&& leftParameters.Value.Count == rightParameters.Value.Count
&& leftParameters.Value.Select((left, index) => left.IsEquivalentTo(rightParameters.Value[index])).All(x => x));
(leftParameters is null && rightParameters is null) // VB.Net
Tim-Pohlmann marked this conversation as resolved.
Show resolved Hide resolved
|| (leftParameters is { Count: { } leftCount } leftParams
&& rightParameters is { Count: { } rightCount } rightParams
&& leftCount == rightCount
&& (leftCount == 0 || HaveSameParameterLists(model, leftParams, rightParams)));
Tim-Pohlmann marked this conversation as resolved.
Show resolved Hide resolved

private static bool HaveSameParameterLists<TSyntax>(SemanticModel model,
SeparatedSyntaxList<TSyntax> leftParameters,
SeparatedSyntaxList<TSyntax> rightParameters) where TSyntax : SyntaxNode =>
leftParameters.Zip(rightParameters, (left, right) => left.IsEquivalentTo(right)).All(x => x) // Perf: Syntactic equivalence for all parameters first
&& leftParameters.Zip(rightParameters, (left, right) => HaveSameParameterType(model, left, right)).All(x => x); // Also make sure the parameter types are same

private static bool HaveSameParameterType(SemanticModel model, SyntaxNode left, SyntaxNode right) =>
model.GetDeclaredSymbol(left) is IParameterSymbol { Type: { } leftParameterType }
&& model.GetDeclaredSymbol(right) is IParameterSymbol { Type: { } rightParameterType }
&& leftParameterType.Equals(rightParameterType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ public sealed class MethodsShouldNotHaveIdenticalImplementations : MethodsShould
protected override IEnumerable<MethodBlockSyntax> GetMethodDeclarations(SyntaxNode node) =>
((TypeBlockSyntax)node).Members.OfType<MethodBlockSyntax>();

protected override bool AreDuplicates(MethodBlockSyntax firstMethod, MethodBlockSyntax secondMethod) =>
protected override bool AreDuplicates(SemanticModel model, MethodBlockSyntax firstMethod, MethodBlockSyntax secondMethod) =>
firstMethod.Statements.Count > 1
&& firstMethod.GetIdentifierText() != secondMethod.GetIdentifierText()
&& HaveSameParameters(firstMethod.GetParameters(), secondMethod.GetParameters())
&& HaveSameParameters(model, firstMethod.GetParameters(), secondMethod.GetParameters())
&& VisualBasicEquivalenceChecker.AreEquivalent(firstMethod.Statements, secondMethod.Statements);

protected override SyntaxToken GetMethodIdentifier(MethodBlockSyntax method) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ public static class TypeConstraints

public static int Use<T>(T? value) where T : class => 2;

public static void First<T>(T? value) where T : struct // Secondary
public static void First<T>(T? value) where T : struct
{
var x = Use(value);
Console.WriteLine(x);
}

public static void Second<T>(T? value) where T : class // Noncompliant - FP, method looks the same but different overloads are called due to the type constraints used. See: https://github.com/SonarSource/sonar-dotnet/issues/7068
public static void Second<T>(T? value) where T : class // Compliant, method looks the same but different overloads are called due to the type constraints used. See: https://github.com/SonarSource/sonar-dotnet/issues/7068
Tim-Pohlmann marked this conversation as resolved.
Show resolved Hide resolved
{
var x = Use(value);
Console.WriteLine(x);
Expand Down
Loading