diff --git a/Rubberduck.CodeAnalysis/Inspections/Abstract/MemberAccessMayReturnNothingInspectionBase.cs b/Rubberduck.CodeAnalysis/Inspections/Abstract/MemberAccessMayReturnNothingInspectionBase.cs index b0defd534a..e4e2389ca8 100644 --- a/Rubberduck.CodeAnalysis/Inspections/Abstract/MemberAccessMayReturnNothingInspectionBase.cs +++ b/Rubberduck.CodeAnalysis/Inspections/Abstract/MemberAccessMayReturnNothingInspectionBase.cs @@ -19,12 +19,20 @@ protected MemberAccessMayReturnNothingInspectionBase(IDeclarationFinderProvider : base(declarationFinderProvider) {} + /// + /// Members that might return Nothing + /// + /// + /// It must not be legal to call the members unqualified. In particular, user-defined members will not be considered. + /// Moreover, this disqualifies all members on global objects. + /// public abstract IEnumerable MembersUnderTest(DeclarationFinder finder); public abstract string ResultTemplate { get; } protected override IEnumerable ObjectionableDeclarations(DeclarationFinder finder) { - return MembersUnderTest(finder); + //This restriction is in place because the inspection currently cannot handle unqualified accesses. + return MembersUnderTest(finder).Where(member => !member.IsUserDefined); } protected override bool IsResultReference(IdentifierReference reference, DeclarationFinder finder) @@ -46,7 +54,7 @@ protected override bool IsResultReference(IdentifierReference reference, Declara { return usageContext is VBAParser.MemberAccessExprContext || !(usageContext is VBAParser.CallStmtContext) - && !ContextIsNothingTest(usageContext); + && !ContextIsNothing(usageContext); } var assignedTo = AssignmentTarget(reference, finder, setter); @@ -65,14 +73,21 @@ private static IdentifierReference AssignmentTarget(IdentifierReference referenc private static RuleContext UsageContext(IdentifierReference reference) { - var access = reference.Context.GetAncestor(); - var usageContext = access.Parent is VBAParser.IndexExprContext indexExpr + //We prefer the with member access over the member access, because the accesses are resolved right to left. + var access = reference.Context.GetAncestor() as VBAParser.LExpressionContext + ?? reference.Context.GetAncestor(); + + if (access == null) + { + return null; + } + + return access.Parent is VBAParser.IndexExprContext indexExpr ? indexExpr.Parent : access.Parent; - return usageContext; } - private static bool ContextIsNothingTest(IParseTree context) + private static bool ContextIsNothing(IParseTree context) { return context is VBAParser.LExprContext && context.Parent is VBAParser.RelationalOpContext comparison @@ -86,7 +101,7 @@ private static bool IsUsedBeforeCheckingForNothing(IdentifierReference assignedT var firstUse = GetReferenceNodes(tree).FirstOrDefault(); return !(firstUse is null) - && !ContextIsNothingTest(firstUse.Reference.Context.Parent); + && !ContextIsNothing(firstUse.Reference.Context.Parent); } private static IEnumerable GetReferenceNodes(INode node) diff --git a/Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml b/Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml index 53fd28924f..6e5313a0af 100644 --- a/Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml +++ b/Rubberduck.Core/UI/Inspections/InspectionResultsControl.xaml @@ -200,7 +200,6 @@ SelectedItem="{Binding SelectedItem}" SelectionUnit="FullRow" ItemsSource="{Binding Results, NotifyOnSourceUpdated=True}" - RequestBringIntoView="InspectionResultsGrid_RequestBringIntoView" VirtualizingPanel.IsVirtualizingWhenGrouping="True" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" diff --git a/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldView.xaml b/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldView.xaml index 7174477f0f..518d258469 100644 --- a/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldView.xaml +++ b/Rubberduck.Core/UI/Refactorings/EncapsulateField/EncapsulateFieldView.xaml @@ -144,6 +144,7 @@ Text="{Binding Path=PropertyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TabIndex="1" Margin="10,5" VerticalAlignment="Center" + VerticalContentAlignment="Center" Height="22" /> diff --git a/Rubberduck.Core/UI/UnitTesting/TestExplorerViewModel.cs b/Rubberduck.Core/UI/UnitTesting/TestExplorerViewModel.cs index b8b14f5d63..97fffd92c5 100644 --- a/Rubberduck.Core/UI/UnitTesting/TestExplorerViewModel.cs +++ b/Rubberduck.Core/UI/UnitTesting/TestExplorerViewModel.cs @@ -256,6 +256,7 @@ public bool CanExecuteIgnoreSelectedTests(object obj) return false; } + public bool CanExecuteUnignoreSelectedTests(object obj) { if (!Model.IsBusy && obj is IList viewModels && viewModels.Count > 0) @@ -273,12 +274,14 @@ public bool CanExecuteIgnoreGroupCommand(object obj) return groupItems.Cast().Count(test => test.Method.IsIgnored) != groupItems.Count; } + public bool CanExecuteUnignoreGroupCommand(object obj) { var groupItems = MouseOverGroup?.Items - ?? GroupContainingSelectedTest(MouseOverTest).Items; - - return groupItems.Cast().Any(test => test.Method.IsIgnored); + ?? GroupContainingSelectedTest(MouseOverTest)?.Items; + + return groupItems != null + && groupItems.Cast().Any(test => test.Method.IsIgnored); } #region Commands diff --git a/Rubberduck.Resources/Inspections/InspectionResults.de.resx b/Rubberduck.Resources/Inspections/InspectionResults.de.resx index 1ace9a322a..e6936ac846 100644 --- a/Rubberduck.Resources/Inspections/InspectionResults.de.resx +++ b/Rubberduck.Resources/Inspections/InspectionResults.de.resx @@ -183,7 +183,7 @@ Der Variable '{0}' wird kein Wert zugewiesen. - vbNullString' sollte statt einem leeren String-Literal verwendet werden. + 'vbNullString' sollte statt einem leeren String-Literal verwendet werden. Objektvariable '{0}' wird ohne das 'Set'-Schlüsselwort zugewiesen. @@ -467,4 +467,4 @@ In Memoriam, 1972-2018 Die Annotation '{0}' erwartet weniger Argumente. - \ No newline at end of file + diff --git a/RubberduckTests/Inspections/ExcelMemberMayReturnNothingInspectionTests.cs b/RubberduckTests/Inspections/ExcelMemberMayReturnNothingInspectionTests.cs index 89925a4948..72a5843a82 100644 --- a/RubberduckTests/Inspections/ExcelMemberMayReturnNothingInspectionTests.cs +++ b/RubberduckTests/Inspections/ExcelMemberMayReturnNothingInspectionTests.cs @@ -26,6 +26,22 @@ End Sub Assert.AreEqual(1, InspectionResults(inputCode).Count()); } + [Test] + [Category("Inspections")] + public void ExcelMemberMayReturnNothing_ReturnsResult_WithMemberAccessOnFind() + { + const string inputCode = + @"Sub UnderTest() + Dim ws As Worksheet + Set ws = Sheet1 + With ws.UsedRange + foo = .Find(""foo"").Row + End With +End Sub +"; + Assert.AreEqual(1, InspectionResults(inputCode).Count()); + } + [Test] [Category("Inspections")] public void ExcelMemberMayReturnNothing_Ignored_DoesNotReturnResult() @@ -111,6 +127,25 @@ End Sub Assert.AreEqual(1, InspectionResults(inputCode).Count()); } + [Test] + [Category("Inspections")] + public void ExcelMemberMayReturnNothing_ReturnsResult_AssignedAndNotTested_FromWithMemberAccess() + { + const string inputCode = + @"Sub UnderTest() + Dim ws As Worksheet + Set ws = Sheet1 + Dim result As Range + With ws.UsedRange + Set result = .Find(""foo"") + End With + result.Value = ""bar"" +End Sub +"; + + Assert.AreEqual(1, InspectionResults(inputCode).Count()); + } + [Test] [Category("Inspections")] public void ExcelMemberMayReturnNothing_ReturnsResult_ResultIsSomethingElse()