Skip to content

Commit e8fb37f

Browse files
committed
Concurrent collections improvements
1 parent 249f88a commit e8fb37f

34 files changed

Lines changed: 388 additions & 28 deletions

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 0.2.12.1
2+
Concurrent collections improvements.
3+
14
## 0.2.12
25
Added concurrent collections diagnostic [CI0007](https://github.com/Backs/Collections.Analyzer/blob/master/Documentation/CI0007.md)
36

Collections.Analyzer/CodeFixes/ReplaceAnyWithIsEmptyCodeFix.cs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
2727
var root = await context.Document.GetSyntaxRootAsync();
2828
var diagnostic = context.Diagnostics.First();
2929

30-
var syntaxNode = root!.FindNode(context.Span) as InvocationExpressionSyntax;
31-
if (syntaxNode?.Expression is not MemberAccessExpressionSyntax memberAccessExpressionSyntax) return;
30+
var memberAccessExpressionSyntax = root!.FindNode(context.Span) as MemberAccessExpressionSyntax;
31+
if (memberAccessExpressionSyntax == null)
32+
return;
3233

3334
var title = Resources.ReplaceWithIsEmpty;
3435

@@ -45,15 +46,29 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
4546
private static async Task<Document> FixAsync(Document document, MemberAccessExpressionSyntax originalExpression,
4647
CancellationToken cancellationToken)
4748
{
48-
var newExpression = PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, MemberAccessExpression(
49-
SyntaxKind.SimpleMemberAccessExpression,
50-
originalExpression.Expression,
51-
IdentifierName(nameof(ConcurrentDictionary<object, object>.IsEmpty)))
52-
)
53-
.NormalizeWhitespace();
49+
ExpressionSyntax newExpression = MemberAccessExpression(
50+
SyntaxKind.SimpleMemberAccessExpression,
51+
originalExpression.Expression,
52+
IdentifierName(nameof(ConcurrentDictionary<object, object>.IsEmpty)));
53+
54+
var expressionParent = originalExpression.Parent;
55+
56+
if (expressionParent?.Parent is PrefixUnaryExpressionSyntax
57+
{
58+
RawKind: (int)SyntaxKind.LogicalNotExpression
59+
})
60+
{
61+
expressionParent = expressionParent.Parent;
62+
}
63+
else
64+
{
65+
newExpression = PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, newExpression);
66+
}
67+
68+
newExpression.NormalizeWhitespace();
5469

5570
var oldRoot = await document.GetSyntaxRootAsync(cancellationToken);
56-
var newRoot = oldRoot!.ReplaceNode(originalExpression.Parent, newExpression);
71+
var newRoot = oldRoot!.ReplaceNode(expressionParent, newExpression);
5772

5873
return document.WithSyntaxRoot(newRoot);
5974
}

Collections.Analyzer/Collections.Analyzer.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<LangVersion>10</LangVersion>
55
<IncludeBuildOutput>false</IncludeBuildOutput>
66
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
7-
<PackageVersion>0.2.12</PackageVersion>
7+
<PackageVersion>0.2.12.1</PackageVersion>
88
<Title>Collections.Analyzer</Title>
99
<Authors>Rogatnev Sergey</Authors>
1010
<Description>Collections.Analyzer is a set of roslyn-based diagnostics for C#-projects that detect potential problems with operating different collections.</Description>

Collections.Analyzer/Diagnostics/CI0007/ConcurrentCollectionIsEmptyDiagnostic.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ private static void Analyze(SyntaxNodeAnalysisContext context)
5757
)
5858
{
5959
context.ReportDiagnostic(Diagnostic.Create(ReplaceAnyWithIsEmptyRule,
60-
invocationExpression.GetLocation(),
60+
invocationExpression.Expression.GetLocation(),
6161
redundantMethod.ToString()));
6262
}
6363
else if (invocationExpression.Expression is MemberAccessExpressionSyntax
@@ -68,7 +68,7 @@ private static void Analyze(SyntaxNodeAnalysisContext context)
6868
&& ConcurrentTypes.Contains(ms.ReturnType.Name))
6969
{
7070
context.ReportDiagnostic(Diagnostic.Create(ReplaceAnyWithIsEmptyRule,
71-
invocationExpression.GetLocation(),
71+
invocationExpression.Expression.GetLocation(),
7272
redundantMethod.ToString()));
7373
}
7474
}

Examples/ConcurrentCollections/ConcurrentBagIsEmpty.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,30 @@ private static ConcurrentBag<string> GetBag()
2828
{
2929
return new ConcurrentBag<string>(new[] { "a", "b", "c" });
3030
}
31+
32+
public bool Method4()
33+
{
34+
var bag = new ConcurrentBag<string>(new[] { "a", "b", "c" });
35+
36+
return !bag.Any();
37+
}
38+
39+
public void Method5()
40+
{
41+
var bag = new ConcurrentBag<string>(new[] { "a", "b", "c" });
42+
43+
var isEmpty = !bag.Any();
44+
}
45+
46+
public void Method6()
47+
{
48+
var bag = new ConcurrentBag<string>(new[] { "a", "b", "c" });
49+
50+
WithEmpty(bag.Any());
51+
}
52+
53+
private void WithEmpty(bool b)
54+
{
55+
throw new System.NotImplementedException();
56+
}
3157
}

Tests/ConcurrentBagIsEmptyTests/IsEmptyTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public Task LocalVariableIsEmptyTest()
1717
var code = ResourceReader.ReadFromFile("ConcurrentBag1.txt");
1818

1919
return ConcurrentBagIsEmptyVerifier
20-
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(12, 20, 12, 29));
20+
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(12, 20, 12, 27));
2121
}
2222

2323
[Test]
@@ -26,7 +26,7 @@ public Task ReturnValueIsEmptyTest()
2626
var code = ResourceReader.ReadFromFile("ConcurrentBag2.txt");
2727

2828
return ConcurrentBagIsEmptyVerifier
29-
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(10, 20, 10, 34));
29+
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(10, 20, 10, 32));
3030
}
3131

3232
[Test]
@@ -41,6 +41,8 @@ public Task IsEmptyNoWarnTest()
4141
[Test]
4242
[TestCase("ConcurrentBagBefore1.txt", "ConcurrentBagAfter1.txt")]
4343
[TestCase("ConcurrentBagBefore2.txt", "ConcurrentBagAfter2.txt")]
44+
[TestCase("ConcurrentBagBefore3.txt", "ConcurrentBagAfter3.txt")]
45+
[TestCase("ConcurrentBagBefore4.txt", "ConcurrentBagAfter4.txt")]
4446
public Task CodeFixesTest(string before, string after)
4547
{
4648
var code = ResourceReader.ReadFromFile(before);

Tests/ConcurrentDictionaryIsEmptyTests/IsEmptyTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public Task LocalVariableIsEmptyTest()
1717
var code = ResourceReader.ReadFromFile("ConcurrentDictionary1.txt");
1818

1919
return ConcurrentDictionaryIsEmptyVerifier
20-
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(15, 20, 15, 30));
20+
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(15, 20, 15, 28));
2121
}
2222

2323
[Test]
@@ -26,7 +26,7 @@ public Task ReturnValueIsEmptyTest()
2626
var code = ResourceReader.ReadFromFile("ConcurrentDictionary3.txt");
2727

2828
return ConcurrentDictionaryIsEmptyVerifier
29-
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(10, 20, 10, 35));
29+
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(10, 20, 10, 33));
3030
}
3131

3232
[Test]
@@ -41,6 +41,8 @@ public Task IsEmptyNoWarnTest()
4141
[Test]
4242
[TestCase("ConcurrentDictionaryBefore1.txt", "ConcurrentDictionaryAfter1.txt")]
4343
[TestCase("ConcurrentDictionaryBefore2.txt", "ConcurrentDictionaryAfter2.txt")]
44+
[TestCase("ConcurrentDictionaryBefore3.txt", "ConcurrentDictionaryAfter3.txt")]
45+
[TestCase("ConcurrentDictionaryBefore4.txt", "ConcurrentDictionaryAfter4.txt")]
4446
public Task CodeFixesTest(string before, string after)
4547
{
4648
var code = ResourceReader.ReadFromFile(before);

Tests/ConcurrentQueueIsEmptyTests/IsEmptyTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public Task LocalVariableIsEmptyTest()
1717
var code = ResourceReader.ReadFromFile("ConcurrentQueue1.txt");
1818

1919
return ConcurrentQueueIsEmptyVerifier
20-
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(12, 20, 12, 31));
20+
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(12, 20, 12, 29));
2121
}
2222

2323
[Test]
@@ -26,7 +26,7 @@ public Task ReturnValueIsEmptyTest()
2626
var code = ResourceReader.ReadFromFile("ConcurrentQueue2.txt");
2727

2828
return ConcurrentQueueIsEmptyVerifier
29-
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(10, 20, 10, 36));
29+
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(10, 20, 10, 34));
3030
}
3131

3232
[Test]
@@ -41,6 +41,8 @@ public Task IsEmptyNoWarnTest()
4141
[Test]
4242
[TestCase("ConcurrentQueueBefore1.txt", "ConcurrentQueueAfter1.txt")]
4343
[TestCase("ConcurrentQueueBefore2.txt", "ConcurrentQueueAfter2.txt")]
44+
[TestCase("ConcurrentQueueBefore3.txt", "ConcurrentQueueAfter3.txt")]
45+
[TestCase("ConcurrentQueueBefore4.txt", "ConcurrentQueueAfter4.txt")]
4446
public Task CodeFixesTest(string before, string after)
4547
{
4648
var code = ResourceReader.ReadFromFile(before);

Tests/ConcurrentStackIsEmptyTests/IsEmptyTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public Task LocalVariableIsEmptyTest()
1717
var code = ResourceReader.ReadFromFile("ConcurrentStack1.txt");
1818

1919
return ConcurrentStackIsEmptyVerifier
20-
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(12, 20, 12, 31));
20+
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(12, 20, 12, 29));
2121
}
2222

2323
[Test]
@@ -26,7 +26,7 @@ public Task ReturnValueIsEmptyTest()
2626
var code = ResourceReader.ReadFromFile("ConcurrentStack2.txt");
2727

2828
return ConcurrentStackIsEmptyVerifier
29-
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(10, 20, 10, 36));
29+
.VerifyAnalyzerAsync(code, DiagnosticResult.CompilerWarning("CI0007").WithSpan(10, 20, 10, 34));
3030
}
3131

3232
[Test]
@@ -41,6 +41,8 @@ public Task IsEmptyNoWarnTest()
4141
[Test]
4242
[TestCase("ConcurrentStackBefore1.txt", "ConcurrentStackAfter1.txt")]
4343
[TestCase("ConcurrentStackBefore2.txt", "ConcurrentStackAfter2.txt")]
44+
[TestCase("ConcurrentStackBefore3.txt", "ConcurrentStackAfter3.txt")]
45+
[TestCase("ConcurrentStackBefore4.txt", "ConcurrentStackAfter4.txt")]
4446
public Task CodeFixesTest(string before, string after)
4547
{
4648
var code = ResourceReader.ReadFromFile(before);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Collections.Concurrent;
2+
using System.Linq;
3+
4+
namespace Examples.ConcurrentCollections
5+
{
6+
public class C
7+
{
8+
public void Method6()
9+
{
10+
var bag = new ConcurrentBag<string>(new[] { "a", "b", "c" });
11+
12+
WithEmpty(!bag.IsEmpty);
13+
}
14+
15+
private void WithEmpty(bool b)
16+
{
17+
throw new System.NotImplementedException();
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)