📄
ReachabilityAnalysis.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Reacher; public sealed class ReachabilityAnalysis(IReadOnlyCollection<Compilation> compilations) { private readonly HashSet<IMethodSymbol> reachableMembers = []; public IReadOnlySet<IMethodSymbol> ReachableMembers => reachableMembers; internal void Analyze(IMethodSymbol member, CancellationToken cancellationToken) { if (!TryGetAssociatedCompilation(member, out var compilation) || !reachableMembers.Add(member)) { return; } foreach (var declaration in member.DeclaringSyntaxReferences) { AnalyzeDeclaration(compilation, declaration, cancellationToken); } } private bool TryGetAssociatedCompilation(IMethodSymbol member, [NotNullWhen(true)] out Compilation? compilation) { compilation = compilations.FirstOrDefault(c => member.DeclaringSyntaxReferences.Any(d => c.ContainsSyntaxTree(d.SyntaxTree)) ); return compilation is not null; } private void AnalyzeDeclaration( Compilation compilation, SyntaxReference declaration, CancellationToken cancellationToken ) { var originalNode = declaration.GetSyntax(cancellationToken); var identifiers = originalNode.DescendantNodes(ShouldDescendInto(originalNode)).OfType<IdentifierNameSyntax>(); var semanticModel = compilation.GetSemanticModel(declaration.SyntaxTree); foreach (var identifier in identifiers) { if (!semanticModel.IsReachable(identifier)) { continue; } if (semanticModel.GetSymbolInfo(identifier, cancellationToken) is not { Symbol: IMethodSymbol member }) { continue; } Analyze(member, cancellationToken); } } private static Func<SyntaxNode, bool> ShouldDescendInto(SyntaxNode originalNode) => syntaxNode => syntaxNode == originalNode || !( syntaxNode.IsKind(SyntaxKind.LocalFunctionStatement) || syntaxNode.IsKind(SyntaxKind.SimpleLambdaExpression) || syntaxNode.IsKind(SyntaxKind.ParenthesizedLambdaExpression) ); }