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 14, 2024
1 parent 6b7d3c5 commit 0f36890
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="SubtypedByTreeNode" xml:space="preserve">
<value>Subtypes</value>
</data>
<data name="InstantiatedByTreeNode" xml:space="preserve">
<value>Instantiated By</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright (C) 2014-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 ClassInterfaceSubtypedByNode : SearchNode {
readonly TypeDef analyzedType;

public ClassInterfaceSubtypedByNode(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.SubtypedByTreeNode);

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) {
foreach (var iface in type.Interfaces) {
if (IsEqual(iface.Interface)) {
yield return new TypeNode(type) { Context = Context };
break;
}
}

yield break;
}

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

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

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

public static bool CanShow(TypeDef type) => (type.IsClass || type.IsInterface) && !type.IsEnum;
}
}
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 (ClassInterfaceSubtypedByNode.CanShow(analyzedType))
yield return new ClassInterfaceSubtypedByNode(analyzedType);

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

0 comments on commit 0f36890

Please sign in to comment.