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

Add CA1868: Unnecessary call to Set.Contains(item) #6767

Merged
merged 46 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
d5e2607
Add analyzer and fixer for CA1865
mpidash Jul 11, 2023
4d10f23
Merge branch 'dotnet:main' into issue-85490-analyzer-unnecessary-set-…
mpidash Jul 13, 2023
5c6bcdb
Add CA1865 to Microsoft.CodeAnalysis.NetAnalyzers.md
mpidash Jul 13, 2023
9547162
Add CA1865 to RulesMissingDocumentation.md
mpidash Jul 13, 2023
52e7f91
Add changes from msbuild pack
mpidash Jul 13, 2023
fea7a0a
Update DoNotGuardSetAddOrRemoveByContainsDescription
mpidash Jul 13, 2023
02fc236
Add ExportCodeFixProviderAttribute
mpidash Jul 14, 2023
68c210f
Remove call to First with call to indexer
mpidash Jul 14, 2023
5a0113f
Change equivalenceKey of CodeAction
mpidash Jul 14, 2023
5e841a6
Check for interface implementation instead of signature match
mpidash Jul 14, 2023
07943c4
Revert "Remove call to First with call to indexer"
mpidash Jul 14, 2023
9b43e00
Remove call to First with call to indexer
mpidash Jul 14, 2023
a8e4875
Add additional tests
mpidash Jul 14, 2023
29a4630
Fix ifs with additional statements
mpidash Jul 14, 2023
468274e
Fix single line ifs in VB
mpidash Jul 14, 2023
326481c
Add fixer case for single line ifs in VB
mpidash Jul 14, 2023
533cd78
Apply suggestions from code review
mpidash Jul 19, 2023
cf49578
CA1865: Add tests for other set types
mpidash Jul 18, 2023
d063408
CA1865: Check descendants instead of switch
mpidash Jul 18, 2023
d7c7c54
CA1865: Do not fire when arguments are different
mpidash Jul 18, 2023
4c71d4c
CA1865: Change fixer title
mpidash Jul 19, 2023
ed09398
CA1865: Update resources
mpidash Jul 19, 2023
4f62beb
WIP: Try to handle IImmutableSet Add and Remove
mpidash Jul 19, 2023
315e2c5
Apply suggestions from code review
mpidash Jul 19, 2023
a0f8d26
CA1865: Use original definitions to filter methods
mpidash Jul 20, 2023
f430939
Merge branch 'main' into issue-85490-analyzer-unnecessary-set-call
mpidash Jul 20, 2023
f22e2dc
Check parameter length before using them
mpidash Jul 22, 2023
efe2993
Only check child operations instead of descendants
mpidash Jul 22, 2023
7c68257
Fix diagnostic message
mpidash Jul 22, 2023
860f217
Also flag when interface types are used
mpidash Jul 22, 2023
8b7a007
Use helper class for required symbols
mpidash Jul 22, 2023
1facbcf
Merge branch 'dotnet:main' into issue-85490-analyzer-unnecessary-set-…
mpidash Jul 22, 2023
ba59f85
Add changes from msbuild pack
mpidash Jul 22, 2023
9513d9d
Only compare first argument value
mpidash Jul 25, 2023
8a3f6ef
Make symbol display format static
mpidash Jul 25, 2023
be9d96e
Typo
mpidash Jul 25, 2023
5fef8d0
Update DoNotGuardSetAddOrRemoveByContainsTitle
mpidash Jul 25, 2023
1d220c6
Change condition to pattern matching
mpidash Jul 25, 2023
c5376f3
Add changes from msbuild pack
mpidash Jul 25, 2023
7a9fa60
Add tests for ternary operators
mpidash Jul 26, 2023
6f69703
Use raw strings and inline class and usings
mpidash Jul 26, 2023
ad82982
Support Add or Remove in else block
mpidash Jul 26, 2023
f723ac5
Support ternary operator (diagnostic only)
mpidash Jul 26, 2023
915a4ad
Move from CA1865 to CA1868
mpidash Jul 26, 2023
8866f5d
Merge branch 'main' into issue-85490-analyzer-unnecessary-set-call
mpidash Jul 27, 2023
953a052
Update src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Perform…
buyaa-n Jul 27, 2023
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
@@ -0,0 +1,74 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.NetCore.Analyzers.Performance;

namespace Microsoft.NetCore.CSharp.Analyzers.Performance
{
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
public sealed class CSharpDoNotGuardSetAddOrRemoveByContainsFixer : DoNotGuardSetAddOrRemoveByContainsFixer
{
protected override bool SyntaxSupportedByFixer(SyntaxNode conditionalSyntax)
{
if (conditionalSyntax is IfStatementSyntax ifStatementSyntax)
{
return ifStatementSyntax.Statement.ChildNodes().Count() == 1;
}

return false;
}

protected override Document ReplaceConditionWithChild(Document document, SyntaxNode root, SyntaxNode conditionalOperationNode, SyntaxNode childOperationNode)
{
SyntaxNode newRoot;

if (conditionalOperationNode is ConditionalExpressionSyntax conditionalExpressionSyntax &&
conditionalExpressionSyntax.WhenFalse.ChildNodes().Any())
{
var expression = GetNegatedExpression(document, childOperationNode);

SyntaxNode newConditionalOperationNode = conditionalExpressionSyntax
.WithCondition((ExpressionSyntax)expression)
.WithWhenTrue(conditionalExpressionSyntax.WhenFalse)
.WithWhenFalse(null!)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❗ This is going to throw ArgumentNullException

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I thought this is for removing the false part, did not notice the '!'. What can we use instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally copied this section from the similar fixer for dictionaries (CSharpDoNotGuardDictionaryRemoveByContainsKeyFixer).
As part of a bugfix for the dictionary analyzer, it was discovered that the case of the ternary operator was never flagged by the analyzer (see #6770 (comment)).
So the code in the fixer that would throw an ArgumentNullException is never executed.

That part should probably be removed from the dictionary fixer as well (I missed that in the PR above):

SyntaxNode newConditionalOperationNode = conditionalExpressionSyntax
.WithCondition((ExpressionSyntax)negatedExpression)
.WithWhenTrue(conditionalExpressionSyntax.WhenFalse)
.WithWhenFalse(null!)
.WithAdditionalAnnotations(Formatter.Annotation).WithTriviaFrom(conditionalOperationNode);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explanation @mpidash, removing this part sounds good to me, what you think @sharwell?

Copy link
Member

@sharwell sharwell Jul 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe if this is reachable, it would would be hit by:

bool added = set.Contains(value) ? false : set.Add(value);

Maybe start by adding the test and seeing what kind of result it gives?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added two tests for ternary operators:

  1. One with a ternary operator as in your example. In this case, no diagnostic is reported.
  2. Another test with a nested ternary operator. No diagnostic is reported for the Add case, but one is reported for the Remove case.

This is because we only check the true part of a conditional for an applicable Add or Remove. So the Add case gets filtered out immediately because it contains no child operations:

var firstOperation = operations.FirstOrDefault();

if (firstOperation is null)
{
    return false;
}

For Remove it depends on the content of the true part. In the simple case, it contains a FieldReferenceOperation, which is not covered by the switch in HasApplicableAddOrRemoveMethod so it produces no diagnostic.
The nested case for Remove produces a diagnostic because the first child operation is a IInvocationOperation.

A ternary operator gets filtered out by the fixer in SyntaxSupportedByFixer, because the childStatementSyntax is not a ExpressionStatementSyntax (it is ConditionalExpressionSyntax). So the part where the ArgumentNullException would be thrown is not reachable.

I think we should filter out ternary operators for now. I am not sure how likely it is that someone would write code like in the test cases, as they already use the return value of Add and Remove.
DoNotGuardDictionaryRemoveByContainsKey should be adapted to treat ternary operators in the same way as this analyzer.


The above has shown a case that is not covered at the moment:

if (MySet.Contains(""Item""))
{
    throw new Exception(""Item already exists"");
}
else
{
    MySet.Add(""Item"");
}

which could be refactored to:

if (!MySet.Add(""Item""))
{
    throw new Exception(""Item already exists"");
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should filter out ternary operators for now. I am not sure how likely it is that someone would write code like in the test cases, as they already use the return value of Add and Remove.
DoNotGuardDictionaryRemoveByContainsKey should be adapted to treat ternary operators in the same way as this analyzer.

Agree that it would be rare, though it could happen and it fixable. I am OK with filtering this out for now and create an issue for future fix.

The above has shown a case that is not covered at the moment:

Did you find why it was not covered and could you fix this with the PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you find why it was not covered and could you fix this with the PR?

Its because atm only the true branch of a condition is checked for Add and Remove.
I will add a fix which also checks the false branch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ad82982 adds support for Add and Remove in else blocks
  • f723ac5 adds support for ternary diagnostics (no fixer)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the fix @mpidash!

@sharwell requested change is addressed PTAL

.WithAdditionalAnnotations(Formatter.Annotation).WithTriviaFrom(conditionalOperationNode);

newRoot = root.ReplaceNode(conditionalOperationNode, newConditionalOperationNode);
}
else if (conditionalOperationNode is IfStatementSyntax ifStatementSyntax && ifStatementSyntax.Else != null)
mpidash marked this conversation as resolved.
Show resolved Hide resolved
{
var expression = GetNegatedExpression(document, childOperationNode);

SyntaxNode newConditionalOperationNode = ifStatementSyntax
.WithCondition((ExpressionSyntax)expression)
.WithStatement(ifStatementSyntax.Else.Statement)
.WithElse(null)
.WithAdditionalAnnotations(Formatter.Annotation).WithTriviaFrom(conditionalOperationNode);

newRoot = root.ReplaceNode(conditionalOperationNode, newConditionalOperationNode);
}
else
{
SyntaxNode newConditionNode = childOperationNode
.WithAdditionalAnnotations(Formatter.Annotation)
.WithTriviaFrom(conditionalOperationNode);

newRoot = root.ReplaceNode(conditionalOperationNode, newConditionNode);
}

return document.WithSyntaxRoot(newRoot);
}

private static SyntaxNode GetNegatedExpression(Document document, SyntaxNode newConditionNode)
{
var generator = SyntaxGenerator.GetGenerator(document);
return generator.LogicalNotExpression(((ExpressionStatementSyntax)newConditionNode).Expression.WithoutTrivia());
}
}
}
1 change: 1 addition & 0 deletions src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
CA1865 | Performance | Info | DoNotGuardSetAddOrRemoveByContains, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1865)
CA2261 | Usage | Warning | DoNotUseConfigureAwaitWithSuppressThrowing, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250)
CA1510 | Maintainability | Info | UseExceptionThrowHelpers, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1510)
CA1511 | Maintainability | Info | UseExceptionThrowHelpers, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1511)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1509,6 +1509,15 @@
<data name="DoNotGuardDictionaryRemoveByContainsKeyTitle" xml:space="preserve">
<value>Unnecessary call to 'Dictionary.ContainsKey(key)'</value>
</data>
<data name="DoNotGuardSetAddOrRemoveByContainsDescription" xml:space="preserve">
<value>Do not guard 'Set.Add(item)' or 'Set.Remove(item)' with 'Set.Contains(item)'. The former two already check whether the item exists and will return if it was added or removed.</value>
mpidash marked this conversation as resolved.
Show resolved Hide resolved
</data>
<data name="DoNotGuardSetAddOrRemoveByContainsMessage" xml:space="preserve">
<value>Do not guard 'Set.Add(item)' or 'Set.Remove(item)' with 'Set.Contains(item)'</value>
mpidash marked this conversation as resolved.
Show resolved Hide resolved
</data>
<data name="DoNotGuardSetAddOrRemoveByContainsTitle" xml:space="preserve">
<value>Unnecessary call to 'Set.Contains(item)'</value>
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
</data>
<data name="RemoveRedundantGuardCallCodeFixTitle" xml:space="preserve">
<value>Remove unnecessary call</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.Threading.Tasks;
using Analyzer.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;

namespace Microsoft.NetCore.Analyzers.Performance
{
public abstract class DoNotGuardSetAddOrRemoveByContainsFixer : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(DoNotGuardSetAddOrRemoveByContains.RuleId);

public sealed override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var node = root.FindNode(context.Span, getInnermostNodeForTie: true);

if (node is null)
{
return;
}

var diagnostic = context.Diagnostics[0];
var conditionalLocation = diagnostic.AdditionalLocations[0];
var childLocation = diagnostic.AdditionalLocations[1];

if (root.FindNode(conditionalLocation.SourceSpan) is not SyntaxNode conditionalSyntax ||
root.FindNode(childLocation.SourceSpan) is not SyntaxNode childStatementSyntax)
{
return;
}

// We only offer a fixer if the conditonal true branch has a single statement, either 'Add' or 'Delete'
if (!SyntaxSupportedByFixer(conditionalSyntax))
{
return;
}

var codeAction = CodeAction.Create(MicrosoftNetCoreAnalyzersResources.DoNotGuardSetAddOrRemoveByContainsTitle,
ct => Task.FromResult(ReplaceConditionWithChild(context.Document, root, conditionalSyntax, childStatementSyntax)),
nameof(MicrosoftNetCoreAnalyzersResources.DoNotGuardSetAddOrRemoveByContainsTitle));

context.RegisterCodeFix(codeAction, diagnostic);
}

protected abstract bool SyntaxSupportedByFixer(SyntaxNode conditionalSyntax);

protected abstract Document ReplaceConditionWithChild(Document document, SyntaxNode root,
SyntaxNode conditionalOperationNode,
SyntaxNode childOperationNode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Analyzer.Utilities.PooledObjects;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.NetCore.Analyzers.Performance
{
using static MicrosoftNetCoreAnalyzersResources;

/// <summary>
/// CA1865: <inheritdoc cref="@DoNotGuardSetAddOrRemoveByContainsTitle"/>
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DoNotGuardSetAddOrRemoveByContains : DiagnosticAnalyzer
{
internal const string RuleId = "CA1865";

private const string Contains = nameof(Contains);
private const string Add = nameof(Add);
private const string Remove = nameof(Remove);

internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(
RuleId,
CreateLocalizableResourceString(nameof(DoNotGuardSetAddOrRemoveByContainsTitle)),
CreateLocalizableResourceString(nameof(DoNotGuardSetAddOrRemoveByContainsMessage)),
DiagnosticCategory.Performance,
RuleLevel.IdeSuggestion,
CreateLocalizableResourceString(nameof(DoNotGuardSetAddOrRemoveByContainsDescription)),
isPortedFxCopRule: false,
isDataflowRule: false);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(OnCompilationStart);
}

private void OnCompilationStart(CompilationStartAnalysisContext context)
{
if (!TryGetRequiredMethods(context.Compilation, out var containsMethod, out var addMethod, out var removeMethod))
{
return;
}

context.RegisterOperationAction(context => OnConditional(context, containsMethod, addMethod, removeMethod), OperationKind.Conditional);
}

private static void OnConditional(OperationAnalysisContext context, IMethodSymbol containsMethod, IMethodSymbol addMethod, IMethodSymbol removeMethod)
{
var conditional = (IConditionalOperation)context.Operation;

if (!TryExtractContainsInvocation(conditional.Condition, containsMethod, out var containsInvocation, out var containsNegated))
{
return;
}

if (!TryExtractAddOrRemoveInvocation(conditional.WhenTrue.Children, addMethod, removeMethod, containsNegated, out var addOrRemoveInvocation))
{
return;
}

if (!AreInvocationsOnSameInstance(containsInvocation, addOrRemoveInvocation))
{
return;
}

using var locations = ArrayBuilder<Location>.GetInstance(2);
locations.Add(conditional.Syntax.GetLocation());
locations.Add(addOrRemoveInvocation.Syntax.Parent!.GetLocation());

context.ReportDiagnostic(containsInvocation.CreateDiagnostic(Rule, additionalLocations: locations.ToImmutable(), null));
}

private static bool TryGetRequiredMethods(
Compilation compilation,
[NotNullWhen(true)] out IMethodSymbol? containsMethod,
[NotNullWhen(true)] out IMethodSymbol? addMethod,
[NotNullWhen(true)] out IMethodSymbol? removeMethod)
{
var iSetType = WellKnownTypeProvider.GetOrCreate(compilation).GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericISet1);
var iCollectionType = WellKnownTypeProvider.GetOrCreate(compilation).GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericICollection1);
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved

if (iSetType is null || iCollectionType is null)
{
containsMethod = null;
addMethod = null;
removeMethod = null;

return false;
}

addMethod = iSetType.GetMembers(Add).OfType<IMethodSymbol>().FirstOrDefault();
containsMethod = iCollectionType.GetMembers(Contains).OfType<IMethodSymbol>().FirstOrDefault();
removeMethod = iCollectionType.GetMembers(Remove).OfType<IMethodSymbol>().FirstOrDefault();
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved

return containsMethod is not null && addMethod is not null && removeMethod is not null;
}

private static bool TryExtractContainsInvocation(
IOperation condition,
IMethodSymbol containsMethod,
[NotNullWhen(true)] out IInvocationOperation? containsInvocation,
out bool containsNegated)
{
containsNegated = false;
containsInvocation = null;

switch (condition.WalkDownParentheses())
{
case IInvocationOperation invocation:
containsInvocation = invocation;
break;
case IUnaryOperation unaryOperation when unaryOperation.OperatorKind == UnaryOperatorKind.Not && unaryOperation.Operand is IInvocationOperation operand:
containsNegated = true;
containsInvocation = operand;
break;
default:
return false;
}

return DoesImplementInterfaceMethod(containsInvocation.TargetMethod, containsMethod);
}

private static bool TryExtractAddOrRemoveInvocation(
IEnumerable<IOperation> operations,
IMethodSymbol addMethod,
IMethodSymbol removeMethod,
bool containsNegated,
[NotNullWhen(true)] out IInvocationOperation? addOrRemoveInvocation)
{
addOrRemoveInvocation = null;
var firstOperation = operations.FirstOrDefault();

if (firstOperation is null)
{
return false;
}

switch (firstOperation)
{
case IInvocationOperation invocation:
if ((containsNegated && DoesImplementInterfaceMethod(invocation.TargetMethod, addMethod)) ||
(!containsNegated && DoesImplementInterfaceMethod(invocation.TargetMethod, removeMethod)))
{
addOrRemoveInvocation = invocation;
return true;
}

break;
case IExpressionStatementOperation expressionStatement:
var nestedAddOrRemove = expressionStatement.Children
.OfType<IInvocationOperation>()
.FirstOrDefault(i => containsNegated ?
DoesImplementInterfaceMethod(i.TargetMethod, addMethod) :
DoesImplementInterfaceMethod(i.TargetMethod, removeMethod));

if (nestedAddOrRemove != null)
{
addOrRemoveInvocation = nestedAddOrRemove;
return true;
}

break;
}

return false;
}

private static bool AreInvocationsOnSameInstance(IInvocationOperation invocation1, IInvocationOperation invocation2)
{
return (invocation1.Instance, invocation2.Instance) switch
{
(IFieldReferenceOperation fieldRef1, IFieldReferenceOperation fieldRef2) => fieldRef1.Member == fieldRef2.Member,
(IPropertyReferenceOperation propRef1, IPropertyReferenceOperation propRef2) => propRef1.Member == propRef2.Member,
(IParameterReferenceOperation paramRef1, IParameterReferenceOperation paramRef2) => paramRef1.Parameter == paramRef2.Parameter,
(ILocalReferenceOperation localRef1, ILocalReferenceOperation localRef2) => localRef1.Local == localRef2.Local,
_ => false,
};
}

private static bool DoesImplementInterfaceMethod(IMethodSymbol method, IMethodSymbol interfaceMethod)
{
return method.IsImplementationOfInterfaceMethod(method.Parameters[0].Type, interfaceMethod.ContainingType, interfaceMethod.Name);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,21 @@ Obecné přetypování (IL unbox.any) používané sekvencí vrácenou metodou E
<target state="translated">Nepotřebné volání Dictionary.ContainsKey(key)</target>
<note />
</trans-unit>
<trans-unit id="DoNotGuardSetAddOrRemoveByContainsDescription">
<source>Do not guard 'Set.Add(item)' or 'Set.Remove(item)' with 'Set.Contains(item)'. The former two already check whether the item exists and will return if it was added or removed.</source>
<target state="new">Do not guard 'Set.Add(item)' or 'Set.Remove(item)' with 'Set.Contains(item)'. The former two already check whether the item exists and will return if it was added or removed.</target>
<note />
</trans-unit>
<trans-unit id="DoNotGuardSetAddOrRemoveByContainsMessage">
<source>Do not guard 'Set.Add(item)' or 'Set.Remove(item)' with 'Set.Contains(item)'</source>
<target state="new">Do not guard 'Set.Add(item)' or 'Set.Remove(item)' with 'Set.Contains(item)'</target>
<note />
</trans-unit>
<trans-unit id="DoNotGuardSetAddOrRemoveByContainsTitle">
<source>Unnecessary call to 'Set.Contains(item)'</source>
<target state="new">Unnecessary call to 'Set.Contains(item)'</target>
<note />
</trans-unit>
<trans-unit id="DoNotHardCodeCertificate">
<source>Do not hard-code certificate</source>
<target state="translated">Nepoužívejte pevně zakódovaný certifikát</target>
Expand Down
Loading