Skip to content

Commit

Permalink
Added Subtype Analyzer
Browse files Browse the repository at this point in the history
Find classes/interfaces that directly subtype the analyzed class
  • Loading branch information
mitchcapper committed Mar 15, 2024
1 parent 6b7d3c5 commit 31c9da4
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 1 deletion.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@
<data name="ImplementedByTreeNode" xml:space="preserve">
<value>Implemented By</value>
</data>
<data name="SubtypesTreeNode" xml:space="preserve">
<value>Subtypes</value>
</data>
<data name="InstantiatedByTreeNode" xml:space="preserve">
<value>Instantiated By</value>
</data>
Expand Down
67 changes: 67 additions & 0 deletions Extensions/dnSpy.Analyzer/TreeNodes/SubtypesNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright (C) 2024 [email protected]
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using dnlib.DotNet;
using dnSpy.Analyzer.Properties;
using dnSpy.Contracts.Decompiler;
using dnSpy.Contracts.Text;

namespace dnSpy.Analyzer.TreeNodes {
sealed class SubtypesNode : SearchNode {
readonly TypeDef analyzedType;

public SubtypesNode(TypeDef analyzedType) =>
this.analyzedType = analyzedType ?? throw new ArgumentNullException(nameof(analyzedType));

protected override void Write(ITextColorWriter output, IDecompiler decompiler) =>
output.Write(BoxedTextColor.Text, dnSpy_Analyzer_Resources.SubtypesTreeNode);

protected override IEnumerable<AnalyzerTreeNodeData> FetchChildren(CancellationToken ct) {
var analyzer = new ScopedWhereUsedAnalyzer<AnalyzerTreeNodeData>(Context.DocumentService, analyzedType, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}

IEnumerable<AnalyzerTreeNodeData> FindReferencesInType(TypeDef type) {
if (analyzedType.IsInterface && type.HasInterfaces) {
for (int i = 0; i < type.Interfaces.Count; i++) {
if (IsEqual(type.Interfaces[i].Interface)) {
yield return new TypeNode(type) { Context = Context };
break;
}
}

yield break;
}

if (type.IsEnum || !type.IsClass)
yield break;

if (IsEqual(type.BaseType))
yield return new TypeNode(type) { Context = Context };
}

private bool IsEqual(ITypeDefOrRef itm) => CheckEquals(analyzedType, itm.GetScopeType());

public static bool CanShow(TypeDef type) => (type.IsClass || type.IsInterface) && !type.IsEnum && !type.IsSealed;
}
}
3 changes: 3 additions & 0 deletions Extensions/dnSpy.Analyzer/TreeNodes/TypeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public override IEnumerable<TreeNodeData> CreateChildren() {
if (TypeExposedByNode.CanShow(analyzedType))
yield return new TypeExposedByNode(analyzedType);

if (SubtypesNode.CanShow(analyzedType))
yield return new SubtypesNode(analyzedType);

if (TypeExtensionMethodsNode.CanShow(analyzedType))
yield return new TypeExtensionMethodsNode(analyzedType);
}
Expand Down

0 comments on commit 31c9da4

Please sign in to comment.