Skip to content

Commit df350f0

Browse files
authored
Merge pull request #5365 from MDoerner/AdditionalTopLevelExceptionHandling
Additional top level exception handling and better status message sequencing
2 parents 2b17c42 + 8840b36 commit df350f0

File tree

4 files changed

+72
-21
lines changed

4 files changed

+72
-21
lines changed

Rubberduck.Core/UI/Command/MenuItems/CommandBars/AppCommandBarBase.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,13 @@ protected ICommandMenuItem FindChildByTag(string tag)
3737
}
3838
catch (COMException exception)
3939
{
40-
Logger.Error(exception,$"COMException while finding child with tag '{tag}'.");
40+
Logger.Error(exception, $"COMException while finding child with tag '{tag}' in the command bar.");
41+
}
42+
catch (InvalidCastException exception)
43+
{
44+
//This exception will be encountered whenever the registration of the command bar control not correct in the system.
45+
//See issue #5349 at https://github.com/rubberduck-vba/Rubberduck/issues/5349
46+
Logger.Error(exception, $"Invalid cast exception while finding child with tag '{tag}' in the command bar.");
4147
}
4248
return null;
4349
}

Rubberduck.Core/UI/Command/MenuItems/CommandBars/RubberduckCommandBar.cs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
using System.Collections.Generic;
33
using System.Globalization;
44
using System.Linq;
5+
using System.Runtime.InteropServices;
56
using Rubberduck.Resources;
6-
using Rubberduck.Parsing;
77
using Rubberduck.Parsing.Symbols;
88
using Rubberduck.Parsing.UIContext;
99
using Rubberduck.Parsing.VBA;
@@ -14,18 +14,18 @@ namespace Rubberduck.UI.Command.MenuItems.CommandBars
1414
public class RubberduckCommandBar : AppCommandBarBase, IDisposable
1515
{
1616
private readonly IContextFormatter _formatter;
17-
private readonly IParseCoordinator _parser;
17+
private readonly RubberduckParserState _state;
1818
private readonly ISelectionChangeService _selectionService;
1919

20-
public RubberduckCommandBar(IParseCoordinator parser, IEnumerable<ICommandMenuItem> items, IContextFormatter formatter, ISelectionChangeService selectionService, IUiDispatcher uiDispatcher)
20+
public RubberduckCommandBar(RubberduckParserState state, IEnumerable<ICommandMenuItem> items, IContextFormatter formatter, ISelectionChangeService selectionService, IUiDispatcher uiDispatcher)
2121
: base("Rubberduck", CommandBarPosition.Top, items, uiDispatcher)
2222
{
23-
_parser = parser;
23+
_state = state;
2424
_formatter = formatter;
2525
_selectionService = selectionService;
2626

27-
_parser.State.StateChanged += OnParserStateChanged;
28-
_parser.State.StatusMessageUpdate += OnParserStatusMessageUpdate;
27+
_state.StateChangedHighPriority += OnParserStateChanged;
28+
_state.StatusMessageUpdate += OnParserStatusMessageUpdate;
2929
_selectionService.SelectionChanged += OnSelectionChange;
3030
}
3131

@@ -35,14 +35,14 @@ public override void Initialize()
3535
{
3636
base.Initialize();
3737
SetStatusLabelCaption(ParserState.Pending);
38-
EvaluateCanExecute(_parser.State);
38+
EvaluateCanExecute(_state);
3939
}
4040

4141
private Declaration _lastDeclaration;
4242
private ParserState _lastStatus = ParserState.None;
4343
private void EvaluateCanExecute(RubberduckParserState state, Declaration selected)
4444
{
45-
var currentStatus = _parser.State.Status;
45+
var currentStatus = _state.Status;
4646
if (_lastStatus == currentStatus &&
4747
(selected == null || selected.Equals(_lastDeclaration)) &&
4848
(selected != null || _lastDeclaration == null))
@@ -68,7 +68,7 @@ private void OnSelectionChange(object sender, DeclarationChangedEventArgs e)
6868
var description = e.Declaration?.DescriptionString ?? string.Empty;
6969
//& renders the next character as if it was an accelerator.
7070
SetContextSelectionCaption(caption?.Replace("&", "&&"), refCount, description);
71-
EvaluateCanExecute(_parser.State, e.Declaration);
71+
EvaluateCanExecute(_state, e.Declaration);
7272
}
7373

7474

@@ -81,14 +81,14 @@ private void OnParserStatusMessageUpdate(object sender, RubberduckStatusMessageE
8181
message = RubberduckUI.ParserState_LoadingReference;
8282
}
8383

84-
SetStatusLabelCaption(message, _parser.State.ModuleExceptions.Count);
84+
SetStatusLabelCaption(message, _state.ModuleExceptions.Count);
8585
}
8686

8787
private void OnParserStateChanged(object sender, EventArgs e)
8888
{
89-
_lastStatus = _parser.State.Status;
90-
EvaluateCanExecute(_parser.State);
91-
SetStatusLabelCaption(_parser.State.Status, _parser.State.ModuleExceptions.Count);
89+
_lastStatus = _state.Status;
90+
EvaluateCanExecute(_state);
91+
SetStatusLabelCaption(_state.Status, _state.ModuleExceptions.Count);
9292
}
9393

9494
public void SetStatusLabelCaption(ParserState state, int? errorCount = null)
@@ -99,11 +99,19 @@ public void SetStatusLabelCaption(ParserState state, int? errorCount = null)
9999

100100
private void SetStatusLabelCaption(string caption, int? errorCount = null)
101101
{
102-
var reparseCommandButton = FindChildByTag(typeof(ReparseCommandMenuItem).FullName) as ReparseCommandMenuItem;
103-
if (reparseCommandButton == null) { return; }
102+
var reparseCommandButton =
103+
FindChildByTag(typeof(ReparseCommandMenuItem).FullName) as ReparseCommandMenuItem;
104+
if (reparseCommandButton == null)
105+
{
106+
return;
107+
}
104108

105-
var showErrorsCommandButton = FindChildByTag(typeof(ShowParserErrorsCommandMenuItem).FullName) as ShowParserErrorsCommandMenuItem;
106-
if (showErrorsCommandButton == null) { return; }
109+
var showErrorsCommandButton =
110+
FindChildByTag(typeof(ShowParserErrorsCommandMenuItem).FullName) as ShowParserErrorsCommandMenuItem;
111+
if (showErrorsCommandButton == null)
112+
{
113+
return;
114+
}
107115

108116
_uiDispatcher.Invoke(() =>
109117
{
@@ -119,7 +127,8 @@ private void SetStatusLabelCaption(string caption, int? errorCount = null)
119127
}
120128
catch (Exception exception)
121129
{
122-
Logger.Error(exception, "Exception thrown trying to set the status label caption on the UI thread.");
130+
Logger.Error(exception,
131+
"Exception thrown trying to set the status label caption on the UI thread.");
123132
}
124133
});
125134
Localize();
@@ -162,8 +171,8 @@ protected virtual void Dispose(bool disposing)
162171
}
163172

164173
_selectionService.SelectionChanged -= OnSelectionChange;
165-
_parser.State.StateChanged -= OnParserStateChanged;
166-
_parser.State.StatusMessageUpdate -= OnParserStatusMessageUpdate;
174+
_state.StateChangedHighPriority -= OnParserStateChanged;
175+
_state.StatusMessageUpdate -= OnParserStatusMessageUpdate;
167176

168177
RemoveCommandBar();
169178

Rubberduck.Core/UI/Inspections/InspectionResultsViewModel.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,20 @@ private void HandleStateChanged(object sender, ParserStateEventArgs e)
428428
}
429429

430430
private async void RefreshInspections(CancellationToken token)
431+
{
432+
//We have to catch all exceptions here since this method is a fire-and-forget async action.
433+
//Accordingly, any exception bubbling out of this method will likely take down the runtime and, thus, crash the host.
434+
try
435+
{
436+
await RefreshInspections_Internal(token);
437+
}
438+
catch (Exception ex)
439+
{
440+
Logger.Error(ex,"Unhandled exception when refreshing inspection results.");
441+
}
442+
}
443+
444+
private async Task RefreshInspections_Internal(CancellationToken token)
431445
{
432446
var stopwatch = Stopwatch.StartNew();
433447
IsBusy = true;

Rubberduck.Parsing/VBA/RubberduckParserState.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ public IReadOnlyList<Tuple<QualifiedModuleName, SyntaxErrorException>> ModuleExc
359359
}
360360
}
361361

362+
public event EventHandler<ParserStateEventArgs> StateChangedHighPriority;
362363
public event EventHandler<ParserStateEventArgs> StateChanged;
363364

364365
private int _stateChangedInvocations;
@@ -367,6 +368,27 @@ private void OnStateChanged(object requestor, CancellationToken token, ParserSta
367368
Interlocked.Increment(ref _stateChangedInvocations);
368369

369370
Logger.Info($"{nameof(RubberduckParserState)} ({_stateChangedInvocations}) is invoking {nameof(StateChanged)} ({Status})");
371+
372+
var highPriorityHandler = StateChangedHighPriority;
373+
if (highPriorityHandler != null && !token.IsCancellationRequested)
374+
{
375+
try
376+
{
377+
var args = new ParserStateEventArgs(state, oldStatus, token);
378+
highPriorityHandler.Invoke(requestor, args);
379+
}
380+
catch (OperationCanceledException cancellation)
381+
{
382+
throw;
383+
}
384+
catch (Exception e)
385+
{
386+
// Error state, because this implies consumers are not exception-safe!
387+
// this behaviour could leave us in a state where some consumers have correctly updated and some have not
388+
Logger.Error(e, "An exception occurred when notifying consumers of updated parser state.");
389+
}
390+
}
391+
370392
var handler = StateChanged;
371393
if (handler != null && !token.IsCancellationRequested)
372394
{

0 commit comments

Comments
 (0)