Core/CompilationExtensions.cs
+8
-4
diff --git a/Core/CompilationExtensions.cs b/Core/CompilationExtensions.cs
index 7341af9..6c0476f 100644
@@ -5,10 +5,14 @@ namespace Reacher;
internal static class CompilationExtensions
{
public static IEnumerable<ISymbol> GetPublicMembers(this Compilation compilation)
public static void Accept(this Compilation compilation, SymbolVisitor visitor) =>
compilation.Assembly.Accept(visitor);
public static void Accept(this IEnumerable<Compilation> compilations, SymbolVisitor visitor)
{
var collector = new PublicMembersCollector();
compilation.Assembly.Accept(collector);
return collector.PublicMembers;
foreach (var compilation in compilations)
{
compilation.Accept(visitor);
}
}
}
Core/DocumentationCommentIdMemberCollector.cs
+0
-63
diff --git a/Core/DocumentationCommentIdMemberCollector.cs b/Core/DocumentationCommentIdMemberCollector.cs
deleted file mode 100644
index abcbce9..0000000
@@ -1,63 +0,0 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
namespace Reacher;
internal class DocumentationCommentIdMemberCollector(HashSet<string> documentationIds) : SymbolVisitor
{
public static IEnumerable<ISymbol> CollectMembers(IEnumerable<string> documentationIds, Compilation[] compilations)
{
var collector = new DocumentationCommentIdMemberCollector([.. documentationIds]);
foreach (var compilation in compilations)
{
compilation.Assembly.Accept(collector);
}
return collector.Members;
}
public IEnumerable<ISymbol> Members => members;
private readonly HashSet<ISymbol> members = [];
public override void VisitAssembly(IAssemblySymbol symbol)
{
symbol.GlobalNamespace.Accept(this);
}
public override void VisitNamespace(INamespaceSymbol symbol)
{
foreach (var namespaceOrType in symbol.GetMembers())
{
namespaceOrType.Accept(this);
}
}
public override void VisitNamedType(INamedTypeSymbol type)
{
foreach (var member in type.GetMembers())
{
member.Accept(this);
}
foreach (var nestedType in type.GetTypeMembers())
{
nestedType.Accept(this);
}
}
public override void VisitMethod(IMethodSymbol method) => VisitMember(method);
public override void VisitProperty(IPropertySymbol property) => VisitMember(property);
private void VisitMember(ISymbol member)
{
if (
member.GetDocumentationCommentId() is not string documentationId
|| !documentationIds.Contains(documentationId)
)
{
return;
}
members.Add(member);
}
}
Core/MemberCollectors/DocumentationCommentIdMemberCollector.cs
+17
-0
diff --git a/Core/MemberCollectors/DocumentationCommentIdMemberCollector.cs b/Core/MemberCollectors/DocumentationCommentIdMemberCollector.cs
new file mode 100644
index 0000000..e8ba1df
@@ -0,0 +1,17 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
namespace Reacher.MemberCollectors;
internal sealed class DocumentationCommentIdMemberCollector(HashSet<string> documentationIds) : MembersCollector
{
public static IEnumerable<ISymbol> CollectMembers(IEnumerable<string> documentationIds, Compilation[] compilations)
{
var collector = new DocumentationCommentIdMemberCollector([.. documentationIds]);
compilations.Accept(collector);
return collector.Members;
}
protected override bool ShouldMemberBeIncluded(ISymbol member) =>
member.GetDocumentationCommentId() is not string documentationId || !documentationIds.Contains(documentationId);
}
Core/MemberCollectors/MembersCollector.cs
+6
-8
diff --git a/Core/ExcludingMembersCollector.cs b/Core/MemberCollectors/MembersCollector.cs
similarity index 87%
rename from Core/ExcludingMembersCollector.cs
rename to Core/MemberCollectors/MembersCollector.cs
index 788fc8f..85e7a55 100644
@@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
namespace Reacher;
namespace Reacher.MemberCollectors;
internal class ExcludingMembersCollector(IReadOnlySet<ISymbol> excludedMembers, Predicate<ISymbol> predicate)
: SymbolVisitor
internal class MembersCollector : SymbolVisitor
{
public IReadOnlySet<ISymbol> Members => members;
private readonly HashSet<ISymbol> members = [];
@@ -42,11 +40,11 @@ internal class ExcludingMembersCollector(IReadOnlySet<ISymbol> excludedMembers,
private void VisitMember(ISymbol member)
{
if (excludedMembers.Contains(member) || !predicate(member))
if (ShouldMemberBeIncluded(member))
{
return;
members.Add(member);
}
members.Add(member);
}
protected virtual bool ShouldMemberBeIncluded(ISymbol member) => true;
}
Core/MemberCollectors/PredicateMembersCollector.cs
+9
-0
diff --git a/Core/MemberCollectors/PredicateMembersCollector.cs b/Core/MemberCollectors/PredicateMembersCollector.cs
new file mode 100644
index 0000000..e5bc290
@@ -0,0 +1,9 @@
using System;
using Microsoft.CodeAnalysis;
namespace Reacher.MemberCollectors;
internal sealed class PredicateMembersCollector(Predicate<ISymbol> predicate) : MembersCollector
{
protected override bool ShouldMemberBeIncluded(ISymbol member) => predicate(member);
}
Core/MemberCollectors/PublicMembersCollector.cs
+17
-0
diff --git a/Core/MemberCollectors/PublicMembersCollector.cs b/Core/MemberCollectors/PublicMembersCollector.cs
new file mode 100644
index 0000000..6ea19fe
@@ -0,0 +1,17 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
namespace Reacher.MemberCollectors;
internal sealed class PublicMembersCollector : MembersCollector
{
public static IEnumerable<ISymbol> CollectMembers(IEnumerable<Compilation> compilations)
{
var collector = new PublicMembersCollector();
compilations.Accept(collector);
return collector.Members;
}
protected override bool ShouldMemberBeIncluded(ISymbol member) =>
member.DeclaredAccessibility is not Accessibility.Public;
}
Core/PublicMembersCollector.cs
+0
-56
diff --git a/Core/PublicMembersCollector.cs b/Core/PublicMembersCollector.cs
deleted file mode 100644
index a39010c..0000000
@@ -1,56 +0,0 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
namespace Reacher;
internal class PublicMembersCollector : SymbolVisitor
{
private readonly HashSet<ISymbol> publicMembers = [];
public IEnumerable<ISymbol> PublicMembers => publicMembers;
public override void VisitAssembly(IAssemblySymbol symbol)
{
symbol.GlobalNamespace.Accept(this);
}
public override void VisitNamespace(INamespaceSymbol symbol)
{
foreach (var namespaceOrType in symbol.GetMembers())
{
namespaceOrType.Accept(this);
}
}
public override void VisitNamedType(INamedTypeSymbol type)
{
if (type.DeclaredAccessibility is not Accessibility.Public)
{
return;
}
foreach (var member in type.GetMembers())
{
member.Accept(this);
}
foreach (var nestedType in type.GetTypeMembers())
{
nestedType.Accept(this);
}
}
public override void VisitMethod(IMethodSymbol method) => VisitMember(method);
public override void VisitProperty(IPropertySymbol property) => VisitMember(property);
private void VisitMember(ISymbol member)
{
if (member.DeclaredAccessibility is not Accessibility.Public)
{
return;
}
publicMembers.Add(member);
}
}
Core/ReachabilityAnalyzer.cs
+3
-7
diff --git a/Core/ReachabilityAnalyzer.cs b/Core/ReachabilityAnalyzer.cs
index f42924f..3cd9b39 100644
@@ -6,6 +6,7 @@ using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Reacher.MemberCollectors;
namespace Reacher;
@@ -87,13 +88,8 @@ internal sealed class ReachabilityAnalyzer(IReadOnlyCollection<Compilation> comp
private IReadOnlySet<ISymbol> GetUnreachableMembers()
{
var collector = new ExcludingMembersCollector(reachableMembers, IsUserDefined);
foreach (var compilation in compilations)
{
compilation.Assembly.Accept(collector);
}
var collector = new PredicateMembersCollector(m => IsUserDefined(m) && !reachableMembers.Contains(m));
compilations.Accept(collector);
return collector.Members;
}
}
Core/SolutionExtensions.cs
+2
-1
diff --git a/Core/SolutionExtensions.cs b/Core/SolutionExtensions.cs
index acfd44a..a568d08 100644
@@ -3,6 +3,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Reacher.MemberCollectors;
namespace Reacher;
@@ -36,7 +37,7 @@ public static class SolutionExtensions
var compilations = await solution.GetCompilations(cancellationToken);
return AnalyzeReachability(
compilations,
[.. compilations.SelectMany(CompilationExtensions.GetPublicMembers)],
PublicMembersCollector.CollectMembers(compilations),
cancellationToken
);
}