From 9056842af727457299d600dea9a2eb6674830496 Mon Sep 17 00:00:00 2001 From: Mitch Capper Date: Thu, 14 Mar 2024 15:28:30 -0700 Subject: [PATCH] Added Subtype Analyzer Find classes/interfaces that directly subtype the analyzed class --- .../dnSpy.Analyzer.Resources.Designer.cs | 11 ++- .../Properties/dnSpy.Analyzer.Resources.resx | 3 + .../TreeNodes/SubtypesByNode.cs | 67 +++++++++++++++++++ .../dnSpy.Analyzer/TreeNodes/TypeNode.cs | 3 + 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 Extensions/dnSpy.Analyzer/TreeNodes/SubtypesByNode.cs diff --git a/Extensions/dnSpy.Analyzer/Properties/dnSpy.Analyzer.Resources.Designer.cs b/Extensions/dnSpy.Analyzer/Properties/dnSpy.Analyzer.Resources.Designer.cs index a0f3a10aa0..45bb054de8 100644 --- a/Extensions/dnSpy.Analyzer/Properties/dnSpy.Analyzer.Resources.Designer.cs +++ b/Extensions/dnSpy.Analyzer/Properties/dnSpy.Analyzer.Resources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -195,6 +195,15 @@ public static string InstantiatedByTreeNode { } } + /// + /// Looks up a localized string similar to Subtypes. + /// + public static string SubtypesByTreeNode { + get { + return ResourceManager.GetString("SubtypesByTreeNode", resourceCulture); + } + } + /// /// Looks up a localized string similar to Overridden By. /// diff --git a/Extensions/dnSpy.Analyzer/Properties/dnSpy.Analyzer.Resources.resx b/Extensions/dnSpy.Analyzer/Properties/dnSpy.Analyzer.Resources.resx index b621766877..c869329d04 100644 --- a/Extensions/dnSpy.Analyzer/Properties/dnSpy.Analyzer.Resources.resx +++ b/Extensions/dnSpy.Analyzer/Properties/dnSpy.Analyzer.Resources.resx @@ -159,6 +159,9 @@ Implemented By + + Subtypes + Instantiated By diff --git a/Extensions/dnSpy.Analyzer/TreeNodes/SubtypesByNode.cs b/Extensions/dnSpy.Analyzer/TreeNodes/SubtypesByNode.cs new file mode 100644 index 0000000000..99cca34dfb --- /dev/null +++ b/Extensions/dnSpy.Analyzer/TreeNodes/SubtypesByNode.cs @@ -0,0 +1,67 @@ +/* + Copyright (C) 2024 mitch.capper+dns@gmail.com + + 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 . +*/ + +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 SubtypesByNode : SearchNode { + readonly TypeDef analyzedType; + + public SubtypesByNode(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.SubtypesByTreeNode); + + protected override IEnumerable FetchChildren(CancellationToken ct) { + var analyzer = new ScopedWhereUsedAnalyzer(Context.DocumentService, analyzedType, FindReferencesInType); + return analyzer.PerformAnalysis(ct); + } + + IEnumerable 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; + } +} diff --git a/Extensions/dnSpy.Analyzer/TreeNodes/TypeNode.cs b/Extensions/dnSpy.Analyzer/TreeNodes/TypeNode.cs index 51d0f96920..c7f12ad201 100644 --- a/Extensions/dnSpy.Analyzer/TreeNodes/TypeNode.cs +++ b/Extensions/dnSpy.Analyzer/TreeNodes/TypeNode.cs @@ -49,6 +49,9 @@ public override IEnumerable CreateChildren() { if (TypeExposedByNode.CanShow(analyzedType)) yield return new TypeExposedByNode(analyzedType); + if (SubtypesByNode.CanShow(analyzedType)) + yield return new SubtypesByNode(analyzedType); + if (TypeExtensionMethodsNode.CanShow(analyzedType)) yield return new TypeExtensionMethodsNode(analyzedType); }