Skip to content

Commit 0498639

Browse files
committed
Identity
1 parent fabb549 commit 0498639

8 files changed

Lines changed: 156 additions & 119 deletions

IDisposableAnalyzers/Helpers/Walkers/DisposableWalker.Assigns.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,19 @@ private static bool Assigns(ExpressionSyntax candidate, Recursion recursion, out
4848
return candidate switch
4949
{
5050
{ Parent: AssignmentExpressionSyntax { Left: { } left, Right: { } right } }
51-
=> right.Contains(candidate) &&
52-
recursion.SemanticModel.TryGetSymbol(left, recursion.CancellationToken, out var assignedSymbol) &&
53-
FieldOrProperty.TryCreate(assignedSymbol, out fieldOrProperty),
51+
=> right.Contains(candidate) &&
52+
recursion.SemanticModel.TryGetSymbol(left, recursion.CancellationToken, out var assignedSymbol) &&
53+
FieldOrProperty.TryCreate(assignedSymbol, out fieldOrProperty),
5454
{ Parent: ArgumentSyntax { Parent: ArgumentListSyntax { Parent: InvocationExpressionSyntax _ } } argument }
55-
=> recursion.Target(argument) is { } target &&
56-
Assigns(target, recursion, out fieldOrProperty) &&
57-
recursion.ContainingType.IsAssignableTo(fieldOrProperty.Symbol.ContainingType, recursion.SemanticModel.Compilation),
55+
=> recursion.Target(argument) is { } target &&
56+
Assigns(target, recursion, out fieldOrProperty) &&
57+
recursion.ContainingType.IsAssignableTo(fieldOrProperty.Symbol.ContainingType, recursion.SemanticModel.Compilation),
5858
{ Parent: EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax variableDeclarator } }
59-
=> recursion.Target(variableDeclarator) is { } target &&
60-
Assigns(target, recursion, out fieldOrProperty),
61-
{ Parent: ExpressionSyntax { } parent }
62-
when IsIdentity(parent)
63-
=> Assigns(parent, recursion, out fieldOrProperty),
59+
=> recursion.Target(variableDeclarator) is { } target &&
60+
Assigns(target, recursion, out fieldOrProperty),
61+
{ }
62+
when Identity(candidate) is { } id
63+
=> Assigns(id, recursion, out fieldOrProperty),
6464
_ => false,
6565
};
6666
}

IDisposableAnalyzers/Helpers/Walkers/DisposableWalker.DisposedByReturnValue.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ when recursion.SemanticModel.TryGetType(objectCreation, recursion.CancellationTo
3333
type == KnownSymbol.CompositeDisposable:
3434
creation = objectCreation;
3535
return true;
36-
case { Parent: ExpressionSyntax { } parent }
37-
when IsIdentity(parent):
38-
creation = parent;
36+
case { }
37+
when Identity(candidate) is { } id:
38+
creation = id;
3939
return true;
4040
case { } expression
4141
when recursion.Target(expression) is { } target &&

IDisposableAnalyzers/Helpers/Walkers/DisposableWalker.Disposes.cs

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -136,36 +136,39 @@ private static bool Disposes<TSource, TSymbol, TNode>(Target<TSource, TSymbol, T
136136

137137
private static bool Disposes(ExpressionSyntax candidate, Recursion recursion)
138138
{
139-
switch (candidate.Parent)
139+
return candidate switch
140140
{
141-
case UsingStatementSyntax _:
142-
case EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax { Parent: UsingStatementSyntax _ } }:
143-
case EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Parent: LocalDeclarationStatementSyntax { UsingKeyword: { ValueText: "using" } } } } }:
144-
return true;
145-
case ConditionalAccessExpressionSyntax { WhenNotNull: InvocationExpressionSyntax invocation }:
146-
return IsDisposeOrReturnValueDisposed(invocation);
147-
case MemberAccessExpressionSyntax { Parent: InvocationExpressionSyntax invocation }:
148-
return IsDisposeOrReturnValueDisposed(invocation);
149-
case ConditionalAccessExpressionSyntax { }:
150-
case MemberAccessExpressionSyntax { }:
151-
return DisposedByReturnValue((ExpressionSyntax)candidate.Parent, recursion, out var creation) &&
152-
Disposes(creation, recursion);
153-
case AssignmentExpressionSyntax { Left: { } left } assignment
154-
when left == candidate:
155-
return Disposes(assignment, recursion);
156-
case EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax variableDeclarator }
157-
when recursion.Target(variableDeclarator) is { } target:
158-
return Disposes(target, recursion);
159-
case ExpressionSyntax parent
160-
when IsIdentity(parent):
161-
return Disposes(parent, recursion);
162-
case ArgumentSyntax argument
163-
when recursion.Target(argument) is { } target:
164-
return DisposedByReturnValue(target, recursion, out var wrapper) &&
165-
Disposes(wrapper, recursion);
166-
}
167-
168-
return false;
141+
{ Parent: UsingStatementSyntax _ }
142+
=> true,
143+
{ Parent: EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax { Parent: UsingStatementSyntax _ } } }
144+
=> true,
145+
{ Parent: EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Parent: LocalDeclarationStatementSyntax { UsingKeyword: { ValueText: "using" } } } } } }
146+
=> true,
147+
{ Parent: ConditionalAccessExpressionSyntax { WhenNotNull: InvocationExpressionSyntax invocation } }
148+
=> IsDisposeOrReturnValueDisposed(invocation),
149+
{ Parent: MemberAccessExpressionSyntax { Parent: InvocationExpressionSyntax invocation } }
150+
=> IsDisposeOrReturnValueDisposed(invocation),
151+
{ Parent: ConditionalAccessExpressionSyntax { } parent }
152+
=> DisposedByReturnValue(parent, recursion, out var creation) &&
153+
Disposes(creation, recursion),
154+
{ Parent: MemberAccessExpressionSyntax { } parent }
155+
=> DisposedByReturnValue(parent, recursion, out var creation) &&
156+
Disposes(creation, recursion),
157+
{ Parent: AssignmentExpressionSyntax { Left: { } left } assignment }
158+
when left == candidate
159+
=> Disposes(assignment, recursion),
160+
{ Parent: EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax variableDeclarator } }
161+
when recursion.Target(variableDeclarator) is { } target
162+
=> Disposes(target, recursion),
163+
{ Parent: ArgumentSyntax argument }
164+
when recursion.Target(argument) is { } target
165+
=> DisposedByReturnValue(target, recursion, out var wrapper) &&
166+
Disposes(wrapper, recursion),
167+
{ }
168+
when Identity(candidate) is { } id
169+
=> Disposes(id, recursion),
170+
_ => false
171+
};
169172

170173
bool IsDisposeOrReturnValueDisposed(InvocationExpressionSyntax invocation)
171174
{
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
namespace IDisposableAnalyzers
2+
{
3+
using System;
4+
using Microsoft.CodeAnalysis.CSharp;
5+
using Microsoft.CodeAnalysis.CSharp.Syntax;
6+
7+
internal sealed partial class DisposableWalker
8+
{
9+
private static ExpressionSyntax? Identity(ExpressionSyntax expression)
10+
{
11+
return expression switch
12+
{
13+
{ Parent: BinaryExpressionSyntax { OperatorToken: { ValueText: "as" } } parent }
14+
=> Recursive(parent),
15+
{ Parent: BinaryExpressionSyntax { OperatorToken: { ValueText: "??" } } parent }
16+
=> Recursive(parent),
17+
{ Parent: CastExpressionSyntax parent }
18+
=> Recursive(parent),
19+
{ Parent: ConditionalExpressionSyntax parent }
20+
=> Recursive(parent),
21+
{ Parent: ParenthesizedExpressionSyntax parent }
22+
=> Recursive(parent),
23+
_ => null,
24+
};
25+
26+
static ExpressionSyntax Recursive(ExpressionSyntax parent) => Identity(parent) ?? parent;
27+
}
28+
29+
[Obsolete("Use Identity")]
30+
private static bool IsIdentity(ExpressionSyntax expression)
31+
{
32+
switch (expression.Kind())
33+
{
34+
case SyntaxKind.AsExpression:
35+
case SyntaxKind.AwaitExpression:
36+
case SyntaxKind.CastExpression:
37+
case SyntaxKind.CoalesceExpression:
38+
case SyntaxKind.ConditionalExpression:
39+
case SyntaxKind.ParenthesizedExpression:
40+
return true;
41+
default:
42+
return false;
43+
}
44+
}
45+
}
46+
}

IDisposableAnalyzers/Helpers/Walkers/DisposableWalker.Ignores.cs

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,56 +9,59 @@
99

1010
internal sealed partial class DisposableWalker
1111
{
12-
internal static bool Ignores(ExpressionSyntax node, SemanticModel semanticModel, CancellationToken cancellationToken)
12+
internal static bool Ignores(ExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken)
1313
{
14-
using var recursion = Recursion.Borrow(node, semanticModel, cancellationToken);
15-
return Ignores(node, recursion);
14+
using var recursion = Recursion.Borrow(candidate, semanticModel, cancellationToken);
15+
return Ignores(candidate, recursion);
1616
}
1717

18-
private static bool Ignores(ExpressionSyntax node, Recursion recursion)
18+
private static bool Ignores(ExpressionSyntax candidate, Recursion recursion)
1919
{
20-
if (Disposes(node, recursion) ||
21-
Assigns(node, recursion, out _) ||
22-
Stores(node, recursion, out _) ||
23-
Returns(node, recursion))
20+
if (Disposes(candidate, recursion) ||
21+
Assigns(candidate, recursion, out _) ||
22+
Stores(candidate, recursion, out _) ||
23+
Returns(candidate, recursion))
2424
{
2525
return false;
2626
}
2727

28-
switch (node.Parent)
28+
return candidate switch
2929
{
30-
case AssignmentExpressionSyntax { Left: IdentifierNameSyntax { Identifier: { ValueText: "_" } } }:
31-
case EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax { Identifier: { ValueText: "_" } } }:
32-
return true;
33-
case AnonymousFunctionExpressionSyntax _:
34-
case UsingStatementSyntax _:
35-
case ReturnStatementSyntax _:
36-
case ArrowExpressionClauseSyntax _:
30+
{ Parent: AssignmentExpressionSyntax { Left: IdentifierNameSyntax { Identifier: { ValueText: "_" } } } }
31+
=> true,
32+
{ Parent: EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax { Identifier: { ValueText: "_" } } } }
33+
=> true,
34+
{ Parent: AnonymousFunctionExpressionSyntax _ }
35+
=> false,
36+
{ Parent: StatementSyntax _ }
37+
=> true,
38+
{ Parent: ArgumentSyntax { Parent: TupleExpressionSyntax tuple } }
39+
=> Ignores(tuple, recursion),
40+
{ Parent: ArgumentSyntax argument }
41+
when recursion.Target(argument) is { } target
42+
=> Ignores(target, recursion),
43+
{ Parent: MemberAccessExpressionSyntax _ }
44+
=> WrappedAndIgnored(),
45+
{ Parent: ConditionalAccessExpressionSyntax _ }
46+
=> WrappedAndIgnored(),
47+
{ Parent: InitializerExpressionSyntax { Parent: ExpressionSyntax creation } }
48+
=> Ignores(creation, recursion),
49+
{ }
50+
when Identity(candidate) is { } id
51+
=> Ignores(id, recursion),
52+
_ => false
53+
};
54+
55+
bool WrappedAndIgnored()
56+
{
57+
if (DisposedByReturnValue(candidate, recursion, out var returnValue) &&
58+
!Ignores(returnValue, recursion))
59+
{
3760
return false;
38-
case StatementSyntax _:
39-
return true;
40-
case ArgumentSyntax { Parent: TupleExpressionSyntax tuple }:
41-
return Ignores(tuple, recursion);
42-
case ArgumentSyntax argument
43-
when recursion.Target(argument) is { } target:
44-
return Ignores(target, recursion);
45-
case MemberAccessExpressionSyntax _:
46-
case ConditionalAccessExpressionSyntax _:
47-
if (DisposedByReturnValue(node, recursion, out var returnValue) &&
48-
!Ignores(returnValue, recursion))
49-
{
50-
return false;
51-
}
61+
}
5262

53-
return true;
54-
case InitializerExpressionSyntax { Parent: ExpressionSyntax creation }:
55-
return Ignores(creation, recursion);
56-
case ExpressionSyntax parent
57-
when IsIdentity(parent):
58-
return Ignores(parent, recursion);
63+
return true;
5964
}
60-
61-
return false;
6265
}
6366

6467
private static bool Ignores(Target<ArgumentSyntax, IParameterSymbol, BaseMethodDeclarationSyntax> target, Recursion recursion)

IDisposableAnalyzers/Helpers/Walkers/DisposableWalker.Returns.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,18 @@ private static bool Returns<TSource, TSymbol, TNode>(Target<TSource, TSymbol, TN
4444

4545
private static bool Returns(ExpressionSyntax candidate, Recursion recursion)
4646
{
47-
return candidate.Parent switch
47+
return candidate switch
4848
{
49-
ReturnStatementSyntax _
50-
=> true,
51-
ArrowExpressionClauseSyntax _
52-
=> true,
53-
EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax variableDeclarator }
54-
=> recursion.Target(variableDeclarator) is { } target &&
55-
Returns(target, recursion),
56-
ExpressionSyntax parent
57-
when IsIdentity(parent) => Returns(parent, recursion),
49+
{ Parent: ReturnStatementSyntax _ }
50+
=> true,
51+
{ Parent: ArrowExpressionClauseSyntax _ }
52+
=> true,
53+
{ Parent: EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax variableDeclarator } }
54+
=> recursion.Target(variableDeclarator) is { } target &&
55+
Returns(target, recursion),
56+
{ }
57+
when Identity(candidate) is { } id
58+
=> Returns(id, recursion),
5859
_ => false,
5960
};
6061
}

IDisposableAnalyzers/Helpers/Walkers/DisposableWalker.Stores.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,18 @@ private static bool Stores<TSource, TSymbol, TNode>(Target<TSource, TSymbol, TNo
4949

5050
private static bool Stores(ExpressionSyntax candidate, Recursion recursion, [NotNullWhen(true)] out ISymbol? container)
5151
{
52-
switch (candidate.Parent)
52+
switch (candidate)
5353
{
54-
case InitializerExpressionSyntax { Parent: ImplicitArrayCreationExpressionSyntax arrayCreation }:
54+
case { Parent: InitializerExpressionSyntax { Parent: ImplicitArrayCreationExpressionSyntax arrayCreation } }:
5555
return StoresOrAssigns(arrayCreation, out container);
56-
case InitializerExpressionSyntax { Parent: ArrayCreationExpressionSyntax arrayCreation }:
56+
case { Parent: InitializerExpressionSyntax { Parent: ArrayCreationExpressionSyntax arrayCreation } }:
5757
return StoresOrAssigns(arrayCreation, out container);
58-
case InitializerExpressionSyntax { Parent: ObjectCreationExpressionSyntax objectInitializer }:
58+
case { Parent: InitializerExpressionSyntax { Parent: ObjectCreationExpressionSyntax objectInitializer } }:
5959
return StoresOrAssigns(objectInitializer, out container);
60-
case AssignmentExpressionSyntax { Right: { } right, Left: ElementAccessExpressionSyntax { Expression: { } element } }
60+
case { Parent: AssignmentExpressionSyntax { Right: { } right, Left: ElementAccessExpressionSyntax { Expression: { } element } } }
6161
when right.Contains(candidate):
6262
return recursion.SemanticModel.TryGetSymbol(element, recursion.CancellationToken, out container);
63-
case ArgumentSyntax { Parent: ArgumentListSyntax { Parent: ObjectCreationExpressionSyntax _ } } argument
63+
case { Parent: ArgumentSyntax { Parent: ArgumentListSyntax { Parent: ObjectCreationExpressionSyntax _ } } argument }
6464
when recursion.Target(argument) is { } target:
6565
if (DisposedByReturnValue(target, recursion, out var objectCreation) ||
6666
AccessibleInReturnValue(target, recursion, out objectCreation))
@@ -70,9 +70,9 @@ when recursion.Target(argument) is { } target:
7070

7171
container = null;
7272
return false;
73-
case ArgumentSyntax { Parent: TupleExpressionSyntax tupleExpression }:
73+
case { Parent: ArgumentSyntax { Parent: TupleExpressionSyntax tupleExpression } }:
7474
return StoresOrAssigns(tupleExpression, out container);
75-
case ArgumentSyntax { Parent: ArgumentListSyntax { Parent: InvocationExpressionSyntax invocation } } argument
75+
case { Parent: ArgumentSyntax { Parent: ArgumentListSyntax { Parent: InvocationExpressionSyntax invocation } } argument }
7676
when recursion.Target(argument) is { Symbol: { } parameter } target:
7777
if (target.TargetNode is null &&
7878
parameter.ContainingType.AllInterfaces.TryFirst(x => x == KnownSymbol.IEnumerable, out _) &&
@@ -107,13 +107,13 @@ when recursion.Target(argument) is { Symbol: { } parameter } target:
107107
container = null;
108108
return false;
109109

110-
case EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax variableDeclarator }
110+
case { Parent: EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax variableDeclarator } }
111111
when recursion.Target(variableDeclarator) is { } target:
112112
return Stores(target, recursion, out container);
113113

114-
case ExpressionSyntax parent
115-
when IsIdentity(parent):
116-
return Stores(parent, recursion, out container);
114+
case { }
115+
when Identity(candidate) is { } id:
116+
return Stores(id, recursion, out container);
117117
default:
118118
container = null;
119119
return false;

IDisposableAnalyzers/Helpers/Walkers/DisposableWalker.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,6 @@ protected override void Clear()
2323
this.usages.Clear();
2424
}
2525

26-
private static bool IsIdentity(ExpressionSyntax expression)
27-
{
28-
switch (expression.Kind())
29-
{
30-
case SyntaxKind.AsExpression:
31-
case SyntaxKind.AwaitExpression:
32-
case SyntaxKind.CastExpression:
33-
case SyntaxKind.CoalesceExpression:
34-
case SyntaxKind.ConditionalExpression:
35-
case SyntaxKind.ParenthesizedExpression:
36-
return true;
37-
default:
38-
return false;
39-
}
40-
}
41-
4226
private static DisposableWalker CreateUsagesWalker(LocalOrParameter localOrParameter, Recursion recursion)
4327
{
4428
if (localOrParameter.TryGetScope(recursion.CancellationToken, out var scope))

0 commit comments

Comments
 (0)