diff --git a/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs b/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs index 138ecd3c1..8a8619080 100644 --- a/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs +++ b/ICSharpCode.ILSpyX/TreeView/SharpTreeNode.cs @@ -21,21 +21,21 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; -#nullable disable - using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; namespace ICSharpCode.ILSpyX.TreeView { public partial class SharpTreeNode : INotifyPropertyChanged { + [AllowNull] protected static ITreeNodeImagesProvider ImagesProvider { get; private set; } public static void SetImagesProvider(ITreeNodeImagesProvider provider) => ImagesProvider = provider; - SharpTreeNodeCollection modelChildren; - internal SharpTreeNode modelParent; + SharpTreeNodeCollection? modelChildren; + internal SharpTreeNode? modelParent; bool isVisible = true; void UpdateIsVisible(bool parentIsVisible, bool updateFlattener) @@ -53,7 +53,7 @@ namespace ICSharpCode.ILSpyX.TreeView node = node.listParent; } // Remember the removed nodes: - List removedNodes = null; + List? removedNodes = null; if (updateFlattener && !newIsVisible) { removedNodes = VisibleDescendantsAndSelf().ToList(); @@ -118,19 +118,19 @@ namespace ICSharpCode.ILSpyX.TreeView } } - public SharpTreeNode Parent { + public SharpTreeNode? Parent { get { return modelParent; } } - public virtual object Text { + public virtual object? Text { get { return null; } } - public virtual object Icon { + public virtual object? Icon { get { return null; } } - public virtual object ToolTip { + public virtual object? ToolTip { get { return null; } } @@ -199,7 +199,7 @@ namespace ICSharpCode.ILSpyX.TreeView while (removeEnd.modelChildren != null && removeEnd.modelChildren.Count > 0) removeEnd = removeEnd.modelChildren.Last(); - List removedNodes = null; + List? removedNodes = null; int visibleIndexOfRemoval = 0; if (node.isVisible) { @@ -221,11 +221,11 @@ namespace ICSharpCode.ILSpyX.TreeView } if (e.NewItems != null) { - SharpTreeNode insertionPos; + SharpTreeNode? insertionPos; if (e.NewStartingIndex == 0) insertionPos = null; else - insertionPos = modelChildren[e.NewStartingIndex - 1]; + insertionPos = modelChildren?[e.NewStartingIndex - 1]; foreach (SharpTreeNode node in e.NewItems) { @@ -260,7 +260,7 @@ namespace ICSharpCode.ILSpyX.TreeView #region Expanding / LazyLoading - public virtual object ExpandedIcon { + public virtual object? ExpandedIcon { get { return Icon; } } @@ -371,13 +371,13 @@ namespace ICSharpCode.ILSpyX.TreeView public IEnumerable Ancestors() { - for (SharpTreeNode n = this.Parent; n != null; n = n.Parent) + for (SharpTreeNode? n = this.Parent; n != null; n = n.Parent) yield return n; } public IEnumerable AncestorsAndSelf() { - for (SharpTreeNode n = this; n != null; n = n.Parent) + for (SharpTreeNode? n = this; n != null; n = n.Parent) yield return n; } @@ -402,7 +402,7 @@ namespace ICSharpCode.ILSpyX.TreeView } } - public virtual string LoadEditText() + public virtual string? LoadEditText() { return null; } @@ -699,7 +699,7 @@ namespace ICSharpCode.ILSpyX.TreeView #region INotifyPropertyChanged Members - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; public void RaisePropertyChanged(string name) { @@ -725,10 +725,10 @@ namespace ICSharpCode.ILSpyX.TreeView { } - public override string ToString() + public override string? ToString() { // used for keyboard navigation - object text = this.Text; + object? text = this.Text; return text != null ? text.ToString() : string.Empty; } } diff --git a/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs b/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs index eece5e01a..c86dc1e80 100644 --- a/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs +++ b/ILSpy/Analyzers/AnalyzerEntityTreeNode.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Collections.Generic; +using System.Diagnostics; using System.Windows; using ICSharpCode.Decompiler.TypeSystem; @@ -25,6 +26,8 @@ using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; +#nullable enable + namespace ICSharpCode.ILSpy.Analyzers { /// @@ -32,7 +35,9 @@ namespace ICSharpCode.ILSpy.Analyzers /// public abstract class AnalyzerEntityTreeNode : AnalyzerTreeNode, IMemberTreeNode { - public abstract IEntity Member { get; } + public abstract IEntity? Member { get; } + + public IEntity? SourceMember { get; protected set; } public override void ActivateItem(IPlatformRoutedEventArgs e) { @@ -43,10 +48,14 @@ namespace ICSharpCode.ILSpy.Analyzers return; } - MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(this.Member.ParentModule?.MetadataFile, this.Member.MetadataToken))); + var module = this.Member.ParentModule?.MetadataFile; + + Debug.Assert(module != null); + + MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(module, this.Member.MetadataToken), this.SourceMember)); } - public override object ToolTip => Member?.ParentModule?.MetadataFile?.FileName; + public override object? ToolTip => Member?.ParentModule?.MetadataFile?.FileName; public override bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies) { @@ -56,13 +65,12 @@ namespace ICSharpCode.ILSpy.Analyzers } foreach (LoadedAssembly asm in removedAssemblies) { - if (this.Member.ParentModule.MetadataFile == asm.GetMetadataFileOrNull()) + if (this.Member.ParentModule!.MetadataFile == asm.GetMetadataFileOrNull()) return false; // remove this node } this.Children.RemoveAll( delegate (SharpTreeNode n) { - AnalyzerTreeNode an = n as AnalyzerTreeNode; - return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies); + return n is not AnalyzerTreeNode an || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies); }); return true; } diff --git a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs index 2cbb18c87..9af81a856 100644 --- a/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs +++ b/ILSpy/Analyzers/AnalyzerSearchTreeNode.cs @@ -76,29 +76,29 @@ namespace ICSharpCode.ILSpy.Analyzers } } - AnalyzerTreeNode SymbolTreeNodeFactory(ISymbol symbol) + AnalyzerTreeNode SymbolTreeNodeFactory(ISymbol resultSymbol) { - if (symbol == null) + if (resultSymbol == null) { - throw new ArgumentNullException(nameof(symbol)); + throw new ArgumentNullException(nameof(resultSymbol)); } - switch (symbol) + switch (resultSymbol) { case IModule module: - return new AnalyzedModuleTreeNode(module) { }; + return new AnalyzedModuleTreeNode(module, (IEntity)this.symbol); case ITypeDefinition td: - return new AnalyzedTypeTreeNode(td) { }; + return new AnalyzedTypeTreeNode(td, (IEntity)this.symbol); case IField fd: - return new AnalyzedFieldTreeNode(fd) { }; + return new AnalyzedFieldTreeNode(fd, (IEntity)this.symbol); case IMethod md: - return new AnalyzedMethodTreeNode(md) { }; + return new AnalyzedMethodTreeNode(md, (IEntity)this.symbol); case IProperty pd: - return new AnalyzedPropertyTreeNode(pd) { }; + return new AnalyzedPropertyTreeNode(pd, (IEntity)this.symbol); case IEvent ed: - return new AnalyzedEventTreeNode(ed) { }; + return new AnalyzedEventTreeNode(ed, (IEntity)this.symbol); default: - throw new ArgumentOutOfRangeException(nameof(symbol), $"Symbol {symbol.GetType().FullName} is not supported."); + throw new ArgumentOutOfRangeException(nameof(resultSymbol), $"Symbol {resultSymbol.GetType().FullName} is not supported."); } } diff --git a/ILSpy/Analyzers/AnalyzerTreeViewModel.cs b/ILSpy/Analyzers/AnalyzerTreeViewModel.cs index c008c0f74..30ef87c99 100644 --- a/ILSpy/Analyzers/AnalyzerTreeViewModel.cs +++ b/ILSpy/Analyzers/AnalyzerTreeViewModel.cs @@ -109,20 +109,20 @@ namespace ICSharpCode.ILSpy.Analyzers switch (entity) { case ITypeDefinition td: - AddOrSelect(new AnalyzedTypeTreeNode(td)); + AddOrSelect(new AnalyzedTypeTreeNode(td, null)); break; case IField fd: if (!fd.IsConst) - AddOrSelect(new AnalyzedFieldTreeNode(fd)); + AddOrSelect(new AnalyzedFieldTreeNode(fd, null)); break; case IMethod md: - AddOrSelect(new AnalyzedMethodTreeNode(md)); + AddOrSelect(new AnalyzedMethodTreeNode(md, null)); break; case IProperty pd: - AddOrSelect(new AnalyzedPropertyTreeNode(pd)); + AddOrSelect(new AnalyzedPropertyTreeNode(pd, null)); break; case IEvent ed: - AddOrSelect(new AnalyzedEventTreeNode(ed)); + AddOrSelect(new AnalyzedEventTreeNode(ed, null)); break; default: throw new ArgumentOutOfRangeException(nameof(entity), $@"Entity {entity.GetType().FullName} is not supported."); diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedAccessorTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedAccessorTreeNode.cs index 64a65633b..08b44dabe 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedAccessorTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedAccessorTreeNode.cs @@ -24,8 +24,8 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { readonly string name; - public AnalyzedAccessorTreeNode(IMethod analyzedMethod, string name) - : base(analyzedMethod) + public AnalyzedAccessorTreeNode(IMethod analyzedMethod, IEntity source, string name) + : base(analyzedMethod, source) { if (string.IsNullOrWhiteSpace(name)) { diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs index dc8ec7714..a197ed0a2 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs @@ -17,10 +17,14 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; +#nullable enable + namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { internal sealed class AnalyzedEventTreeNode : AnalyzerEntityTreeNode @@ -28,11 +32,12 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes readonly IEvent analyzedEvent; readonly string prefix; - public AnalyzedEventTreeNode(IEvent analyzedEvent, string prefix = "") + public AnalyzedEventTreeNode(IEvent analyzedEvent, IEntity? source, string prefix = "") { this.analyzedEvent = analyzedEvent ?? throw new ArgumentNullException(nameof(analyzedEvent)); this.prefix = prefix; this.LazyLoading = true; + this.SourceMember = source; } public override IEntity Member => analyzedEvent; @@ -45,15 +50,16 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes protected override void LoadChildren() { if (analyzedEvent.CanAdd) - this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.AddAccessor, "add")); + this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.AddAccessor, this.SourceMember, "add")); if (analyzedEvent.CanRemove) - this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.RemoveAccessor, "remove")); + this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.RemoveAccessor, this.SourceMember, "remove")); if (TryFindBackingField(analyzedEvent, out var backingField)) - this.Children.Add(new AnalyzedFieldTreeNode(backingField)); + this.Children.Add(new AnalyzedFieldTreeNode(backingField, this.SourceMember)); foreach (var lazy in Analyzers) { var analyzer = lazy.Value; + Debug.Assert(analyzer != null); if (analyzer.Show(analyzedEvent)) { this.Children.Add(new AnalyzerSearchTreeNode(analyzedEvent, analyzer, lazy.Metadata?.Header)); @@ -61,10 +67,10 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes } } - bool TryFindBackingField(IEvent analyzedEvent, out IField backingField) + bool TryFindBackingField(IEvent analyzedEvent, [NotNullWhen(true)] out IField? backingField) { backingField = null; - foreach (var field in analyzedEvent.DeclaringTypeDefinition.GetFields(options: GetMemberOptions.IgnoreInheritedMembers)) + foreach (var field in analyzedEvent.DeclaringTypeDefinition?.GetFields(options: GetMemberOptions.IgnoreInheritedMembers) ?? []) { if (field.Name == analyzedEvent.Name && field.Accessibility == Decompiler.TypeSystem.Accessibility.Private) { diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs index 89858ef8c..48831a67c 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs @@ -17,19 +17,23 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; +#nullable enable + namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { class AnalyzedFieldTreeNode : AnalyzerEntityTreeNode { readonly IField analyzedField; - public AnalyzedFieldTreeNode(IField analyzedField) + public AnalyzedFieldTreeNode(IField analyzedField, IEntity? source) { this.analyzedField = analyzedField ?? throw new ArgumentNullException(nameof(analyzedField)); + this.SourceMember = source; this.LazyLoading = true; } @@ -42,6 +46,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes foreach (var lazy in Analyzers) { var analyzer = lazy.Value; + Debug.Assert(analyzer != null); if (analyzer.Show(analyzedField)) { this.Children.Add(new AnalyzerSearchTreeNode(analyzedField, analyzer, lazy.Metadata?.Header)); diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs index 2186517bf..421c9bf2d 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs @@ -17,10 +17,13 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; +#nullable enable + namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { internal class AnalyzedMethodTreeNode : AnalyzerEntityTreeNode @@ -28,9 +31,10 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes readonly IMethod analyzedMethod; readonly string prefix; - public AnalyzedMethodTreeNode(IMethod analyzedMethod, string prefix = "") + public AnalyzedMethodTreeNode(IMethod analyzedMethod, IEntity? source, string prefix = "") { this.analyzedMethod = analyzedMethod ?? throw new ArgumentNullException(nameof(analyzedMethod)); + this.SourceMember = source; this.prefix = prefix; this.LazyLoading = true; } @@ -44,9 +48,10 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes foreach (var lazy in Analyzers) { var analyzer = lazy.Value; + Debug.Assert(analyzer != null); if (analyzer.Show(analyzedMethod)) { - this.Children.Add(new AnalyzerSearchTreeNode(analyzedMethod, analyzer, lazy.Metadata.Header)); + this.Children.Add(new AnalyzerSearchTreeNode(analyzedMethod, analyzer, lazy.Metadata!.Header)); } } } diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs index 8a9030d0e..7695b625f 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Windows; using ICSharpCode.Decompiler.TypeSystem; @@ -25,15 +26,18 @@ using ICSharpCode.ILSpyX; using ICSharpCode.ILSpyX.TreeView; using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; +#nullable enable + namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { internal class AnalyzedModuleTreeNode : AnalyzerEntityTreeNode { readonly IModule analyzedModule; - public AnalyzedModuleTreeNode(IModule analyzedModule) + public AnalyzedModuleTreeNode(IModule analyzedModule, IEntity? source) { this.analyzedModule = analyzedModule ?? throw new ArgumentNullException(nameof(analyzedModule)); + this.SourceMember = source; this.LazyLoading = true; } @@ -41,16 +45,17 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes public override object Text => analyzedModule.AssemblyName; - public override object ToolTip => analyzedModule.MetadataFile?.FileName; + public override object? ToolTip => analyzedModule.MetadataFile?.FileName; protected override void LoadChildren() { foreach (var lazy in Analyzers) { var analyzer = lazy.Value; + Debug.Assert(analyzer != null); if (analyzer.Show(analyzedModule)) { - this.Children.Add(new AnalyzerSearchTreeNode(analyzedModule, analyzer, lazy.Metadata.Header)); + this.Children.Add(new AnalyzerSearchTreeNode(analyzedModule, analyzer, lazy.Metadata!.Header)); } } } @@ -66,7 +71,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes MessageBus.Send(this, new NavigateToReferenceEventArgs(analyzedModule.MetadataFile)); } - public override IEntity Member => null; + public override IEntity? Member => null; public override bool HandleAssemblyListChanged(ICollection removedAssemblies, ICollection addedAssemblies) { @@ -81,8 +86,7 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes } this.Children.RemoveAll( delegate (SharpTreeNode n) { - AnalyzerTreeNode an = n as AnalyzerTreeNode; - return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies); + return n is not AnalyzerTreeNode an || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies); }); return true; } diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs index 537a166cb..4de437182 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs @@ -17,10 +17,13 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; +#nullable enable + namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { sealed class AnalyzedPropertyTreeNode : AnalyzerEntityTreeNode @@ -28,11 +31,12 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes readonly IProperty analyzedProperty; readonly string prefix; - public AnalyzedPropertyTreeNode(IProperty analyzedProperty, string prefix = "") + public AnalyzedPropertyTreeNode(IProperty analyzedProperty, IEntity? source, string prefix = "") { this.analyzedProperty = analyzedProperty ?? throw new ArgumentNullException(nameof(analyzedProperty)); this.prefix = prefix; this.LazyLoading = true; + this.SourceMember = source; } public override object Icon => PropertyTreeNode.GetIcon(analyzedProperty); @@ -43,16 +47,17 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes protected override void LoadChildren() { if (analyzedProperty.CanGet) - this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Getter, "get")); + this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Getter, this.SourceMember, "get")); if (analyzedProperty.CanSet) - this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Setter, "set")); + this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Setter, this.SourceMember, "set")); foreach (var lazy in Analyzers) { var analyzer = lazy.Value; + Debug.Assert(analyzer != null); if (analyzer.Show(analyzedProperty)) { - this.Children.Add(new AnalyzerSearchTreeNode(analyzedProperty, analyzer, lazy.Metadata.Header)); + this.Children.Add(new AnalyzerSearchTreeNode(analyzedProperty, analyzer, lazy.Metadata!.Header)); } } } diff --git a/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs b/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs index e87fb9938..c4cd300ce 100644 --- a/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs +++ b/ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs @@ -17,19 +17,23 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.ILSpy.TreeNodes; +#nullable enable + namespace ICSharpCode.ILSpy.Analyzers.TreeNodes { internal class AnalyzedTypeTreeNode : AnalyzerEntityTreeNode { readonly ITypeDefinition analyzedType; - public AnalyzedTypeTreeNode(ITypeDefinition analyzedType) + public AnalyzedTypeTreeNode(ITypeDefinition analyzedType, IEntity? source) { this.analyzedType = analyzedType ?? throw new ArgumentNullException(nameof(analyzedType)); + this.SourceMember = source; this.LazyLoading = true; } @@ -42,9 +46,10 @@ namespace ICSharpCode.ILSpy.Analyzers.TreeNodes foreach (var lazy in Analyzers) { var analyzer = lazy.Value; + Debug.Assert(analyzer != null); if (analyzer.Show(analyzedType)) { - this.Children.Add(new AnalyzerSearchTreeNode(analyzedType, analyzer, lazy.Metadata.Header)); + this.Children.Add(new AnalyzerSearchTreeNode(analyzedType, analyzer, lazy.Metadata!.Header)); } } } diff --git a/ILSpy/AssemblyTree/AssemblyTreeModel.cs b/ILSpy/AssemblyTree/AssemblyTreeModel.cs index 43a5be4d9..c201179c1 100644 --- a/ILSpy/AssemblyTree/AssemblyTreeModel.cs +++ b/ILSpy/AssemblyTree/AssemblyTreeModel.cs @@ -67,6 +67,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree private readonly NavigationHistory history = new(); private NavigationState? navigatingToState; + private object? sourceOfReference; private readonly SettingsService settingsService; private readonly LanguageService languageService; private readonly IExportProvider exportProvider; @@ -262,7 +263,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree found = true; if (SelectedItem == initialSelection) { - await JumpToReferenceAsync(mr); + await JumpToReferenceAsync(mr, null); } } } @@ -601,7 +602,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree List path = new List(); while (node.Parent != null) { - path.Add(node.ToString()); + path.Add(node.ToString()!); node = node.Parent; } path.Reverse(); @@ -642,7 +643,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree private void JumpToReference(object? sender, NavigateToReferenceEventArgs e) { - JumpToReferenceAsync(e.Reference, e.InNewTabPage).HandleExceptions(); + JumpToReferenceAsync(e.Reference, e.Source, e.InNewTabPage).HandleExceptions(); IsActive = true; } @@ -653,8 +654,9 @@ namespace ICSharpCode.ILSpy.AssemblyTree /// Returns a task that will signal completion when the decompilation of the jump target has finished. /// The task will be marked as canceled if the decompilation is canceled. /// - private Task JumpToReferenceAsync(object? reference, bool inNewTabPage = false) + private Task JumpToReferenceAsync(object? reference, object? source, bool inNewTabPage = false) { + this.sourceOfReference = source; var decompilationTask = Task.CompletedTask; switch (reference) @@ -804,6 +806,8 @@ namespace ICSharpCode.ILSpy.AssemblyTree public void DecompileSelectedNodes(ViewState? newState = null) { + object? source = this.sourceOfReference; + this.sourceOfReference = null; var activeTabPage = DockWorkspace.ActiveTabPage; if (activeTabPage.FrozenContent) @@ -831,7 +835,9 @@ namespace ICSharpCode.ILSpy.AssemblyTree var options = activeTabPage.CreateDecompilationOptions(); options.TextViewState = newState as DecompilerTextViewState; - activeTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, options)); + activeTabPage.ShowTextViewAsync(textView => { + return textView.DecompileAsync(this.CurrentLanguage, this.SelectedNodes, source, options); + }); } public void RefreshDecompiledView() diff --git a/ILSpy/TextView/AvalonEditTextOutput.cs b/ILSpy/TextView/AvalonEditTextOutput.cs index d3d8efbba..6882b2c8b 100644 --- a/ILSpy/TextView/AvalonEditTextOutput.cs +++ b/ILSpy/TextView/AvalonEditTextOutput.cs @@ -312,6 +312,13 @@ namespace ICSharpCode.ILSpy.TextView references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference, IsLocal = true, IsDefinition = isDefinition }); } + internal object InitialHighlightReference = null; + + public void SetInitialHighlight(object reference) + { + this.InitialHighlightReference = reference; + } + public void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false, bool isDefinition = false) { WriteIndent(); diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 55c97088d..f8da82342 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -802,6 +802,7 @@ namespace ICSharpCode.ILSpy.TextView currentAddress = textOutput.Address; currentTitle = textOutput.Title; expandMemberDefinitions = settingsService.DisplaySettings.ExpandMemberDefinitions; + SetLocalReferenceMarks(textOutput.InitialHighlightReference); } #endregion @@ -817,7 +818,7 @@ namespace ICSharpCode.ILSpy.TextView [Obsolete("Use DecompileAsync() instead")] public void Decompile(ILSpy.Language language, IEnumerable treeNodes, DecompilationOptions options) { - DecompileAsync(language, treeNodes, options).HandleExceptions(); + DecompileAsync(language, treeNodes, null, options).HandleExceptions(); } /// @@ -826,7 +827,7 @@ namespace ICSharpCode.ILSpy.TextView /// If any errors occur, the error message is displayed in the text view, and the task returned by this method completes successfully. /// If the operation is cancelled (by starting another decompilation action); the returned task is marked as cancelled. /// - public Task DecompileAsync(ILSpy.Language language, IEnumerable treeNodes, DecompilationOptions options) + public Task DecompileAsync(ILSpy.Language language, IEnumerable treeNodes, object? source, DecompilationOptions options) { // Some actions like loading an assembly list cause several selection changes in the tree view, // and each of those will start a decompilation action. @@ -834,7 +835,7 @@ namespace ICSharpCode.ILSpy.TextView bool isDecompilationScheduled = this.nextDecompilationRun != null; if (this.nextDecompilationRun != null) this.nextDecompilationRun.TaskCompletionSource.TrySetCanceled(); - this.nextDecompilationRun = new DecompilationContext(language, treeNodes.ToArray(), options); + this.nextDecompilationRun = new DecompilationContext(language, treeNodes.ToArray(), options, source); var task = this.nextDecompilationRun.TaskCompletionSource.Task; if (!isDecompilationScheduled) { @@ -857,12 +858,14 @@ namespace ICSharpCode.ILSpy.TextView public readonly ILSpyTreeNode[] TreeNodes; public readonly DecompilationOptions Options; public readonly TaskCompletionSource TaskCompletionSource = new TaskCompletionSource(); + public readonly object? Source; - public DecompilationContext(ILSpy.Language language, ILSpyTreeNode[] treeNodes, DecompilationOptions options) + public DecompilationContext(ILSpy.Language language, ILSpyTreeNode[] treeNodes, DecompilationOptions options, object? source = null) { this.Language = language; this.TreeNodes = treeNodes; this.Options = options; + this.Source = source; } } @@ -914,6 +917,7 @@ namespace ICSharpCode.ILSpy.TextView { AvalonEditTextOutput textOutput = new AvalonEditTextOutput(); textOutput.LengthLimit = outputLengthLimit; + textOutput.SetInitialHighlight(context.Source); DecompileNodes(context, textOutput); textOutput.PrepareDocument(); tcs.SetResult(textOutput); @@ -994,19 +998,7 @@ namespace ICSharpCode.ILSpy.TextView if (referenceSegment.IsLocal) { ClearLocalReferenceMarks(); - if (references != null) - { - foreach (var r in references) - { - if (reference.Equals(r.Reference)) - { - - var mark = textMarkerService.Create(r.StartOffset, r.Length); - mark.BackgroundColor = (Color)(r.IsDefinition ? FindResource(ResourceKeys.TextMarkerDefinitionBackgroundColor) : FindResource(ResourceKeys.TextMarkerBackgroundColor)); - localReferenceMarks.Add(mark); - } - } - } + SetLocalReferenceMarks(reference); return; } if (definitionLookup != null) @@ -1027,6 +1019,23 @@ namespace ICSharpCode.ILSpy.TextView MessageBus.Send(this, new NavigateToReferenceEventArgs(reference, openInNewTab)); } + private void SetLocalReferenceMarks(object reference) + { + if (references == null || reference == null) + { + return; + } + foreach (var r in references) + { + if (reference.Equals(r.Reference)) + { + var mark = textMarkerService.Create(r.StartOffset, r.Length); + mark.BackgroundColor = (Color)(r.IsDefinition ? FindResource(ResourceKeys.TextMarkerDefinitionBackgroundColor) : FindResource(ResourceKeys.TextMarkerBackgroundColor)); + localReferenceMarks.Add(mark); + } + } + } + Point? mouseDownPos; void TextAreaMouseDown(object sender, MouseButtonEventArgs e) @@ -1091,7 +1100,11 @@ namespace ICSharpCode.ILSpy.TextView SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = language.FileExtension; dlg.Filter = language.Name + "|*" + language.FileExtension + Properties.Resources.AllFiles; - dlg.FileName = WholeProjectDecompiler.CleanUpFileName(treeNodes.First().Text.ToString(), language.FileExtension); + string? nodeText = treeNodes.First().Text?.ToString(); + if (!string.IsNullOrWhiteSpace(nodeText)) + { + dlg.FileName = WholeProjectDecompiler.CleanUpFileName(nodeText, language.FileExtension); + } if (dlg.ShowDialog() == true) { SaveToDisk(new DecompilationContext(language, treeNodes.ToArray(), options), dlg.FileName); diff --git a/ILSpy/Util/MessageBus.cs b/ILSpy/Util/MessageBus.cs index 871205901..c6c24b362 100644 --- a/ILSpy/Util/MessageBus.cs +++ b/ILSpy/Util/MessageBus.cs @@ -79,10 +79,10 @@ namespace ICSharpCode.ILSpy.Util public class SettingsChangedEventArgs(PropertyChangedEventArgs e) : WrappedEventArgs(e); - public class NavigateToReferenceEventArgs(object reference, bool inNewTabPage = false) : EventArgs + public class NavigateToReferenceEventArgs(object reference, object? source = null, bool inNewTabPage = false) : EventArgs { public object Reference { get; } = reference; - + public object? Source { get; } = source; public bool InNewTabPage { get; } = inNewTabPage; } diff --git a/ILSpy/Views/DebugSteps.xaml.cs b/ILSpy/Views/DebugSteps.xaml.cs index da9940c3a..87bcd0859 100644 --- a/ILSpy/Views/DebugSteps.xaml.cs +++ b/ILSpy/Views/DebugSteps.xaml.cs @@ -140,7 +140,7 @@ namespace ICSharpCode.ILSpy } var state = dockWorkspace.ActiveTabPage.GetState(); - dockWorkspace.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(assemblyTreeModel.CurrentLanguage, assemblyTreeModel.SelectedNodes, + dockWorkspace.ActiveTabPage.ShowTextViewAsync(textView => textView.DecompileAsync(assemblyTreeModel.CurrentLanguage, assemblyTreeModel.SelectedNodes, null, new DecompilationOptions(assemblyTreeModel.CurrentLanguageVersion, settingsService.DecompilerSettings, settingsService.DisplaySettings) { StepLimit = step, IsDebug = isDebug,