Skip to content

Commit dc4e54b

Browse files
committed
closes #3449
1 parent 23384d8 commit dc4e54b

File tree

6 files changed

+220
-4
lines changed

6 files changed

+220
-4
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using Rubberduck.CodeAnalysis.Inspections.Abstract;
2+
using Rubberduck.CodeAnalysis.Inspections.Attributes;
3+
using Rubberduck.Parsing;
4+
using Rubberduck.Parsing.Grammar;
5+
using Rubberduck.Parsing.Symbols;
6+
using Rubberduck.Parsing.VBA;
7+
using Rubberduck.Parsing.VBA.DeclarationCaching;
8+
using Rubberduck.Resources.Inspections;
9+
using System.Collections.Generic;
10+
using System.Globalization;
11+
using System.Linq;
12+
13+
namespace Rubberduck.CodeAnalysis.Inspections.Concrete
14+
{
15+
/// <summary>
16+
/// Identifies parameterless 'Range.Cells' member calls.
17+
/// </summary>
18+
/// <reference name="Excel" />
19+
/// <why>
20+
/// Range.Cells is a parameterized Property Get procedure that accepts RowIndex and ColumnIndex parameters, both optional
21+
/// to avoid requiring either when only one needs to be supplied. If no parameters are provided,
22+
/// Cells simply returns a reference to the parent Range object, making a parameterless call entirely redundant.
23+
/// </why>
24+
/// <example hasResult="true">
25+
/// <module name="MyModule" type="Standard Module">
26+
/// <![CDATA[
27+
/// Option Explicit
28+
///
29+
/// Public Sub DoSomething()
30+
/// Debug.Print Sheet1.Range("A1").Cells.Address
31+
/// End Sub
32+
/// ]]>
33+
/// </module>
34+
/// </example>
35+
/// <example hasResult="false">
36+
/// <module name="MyModule" type="Standard Module">
37+
/// <![CDATA[
38+
/// Option Explicit
39+
///
40+
/// Public Sub DoSomething()
41+
/// Debug.Print Sheet1.Range("A1").Address
42+
/// End Sub
43+
/// ]]>
44+
/// </module>
45+
/// </example>
46+
[RequiredLibrary("Excel")]
47+
internal sealed class ParameterlessCellsInspection : IdentifierReferenceInspectionBase
48+
{
49+
public ParameterlessCellsInspection(IDeclarationFinderProvider declarationFinderProvider)
50+
: base(declarationFinderProvider)
51+
{
52+
}
53+
54+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults(DeclarationFinder finder)
55+
{
56+
var excel = finder.Projects.SingleOrDefault(item => !item.IsUserDefined && item.IdentifierName == "Excel");
57+
if (excel is null)
58+
{
59+
yield break;
60+
}
61+
62+
var range = finder.Classes.SingleOrDefault(item => !item.IsUserDefined && item.IdentifierName == "Range" && item.ProjectId == excel.ProjectId);
63+
if (range is null)
64+
{
65+
yield break;
66+
}
67+
68+
var cells = finder.Members(range).SingleOrDefault(item => item.IdentifierName == "Cells" && item.DeclarationType == DeclarationType.PropertyGet);
69+
if (cells is null)
70+
{
71+
yield break;
72+
}
73+
74+
foreach (var reference in cells.References.Where(reference => IsResultReference(reference, finder)))
75+
{
76+
yield return InspectionResult(reference, finder);
77+
}
78+
}
79+
80+
protected override bool IsResultReference(IdentifierReference reference, DeclarationFinder finder)
81+
{
82+
var memberAccess = reference.Context.GetAncestor<VBAParser.MemberAccessExprContext>();
83+
var memberArgs = memberAccess?.GetAncestor<VBAParser.IndexExprContext>()?.argumentList()?.argument();
84+
85+
return memberAccess is VBAParser.MemberAccessExprContext && (memberArgs?.Length ?? 0) == 0;
86+
}
87+
88+
protected override string ResultDescription(IdentifierReference reference)
89+
{
90+
return InspectionResults.ResourceManager.GetString(nameof(ParameterlessCellsInspection), CultureInfo.CurrentUICulture);
91+
}
92+
}
93+
}

Rubberduck.Resources/Inspections/InspectionResults.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rubberduck.Resources/Inspections/InspectionResults.fr.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,4 +489,7 @@ In memoriam, 1972-2018</value>
489489
<data name="InconsistentParamArrayBaseInspection" xml:space="preserve">
490490
<value>Le paramètre ParamArray '{0}' est indexé à partir de zéro</value>
491491
</data>
492+
<data name="ParameterlessCellsInspection" xml:space="preserve">
493+
<value>L'appel non paramétré à 'Range.Cells' est redondant.</value>
494+
</data>
492495
</root>

Rubberduck.Resources/Inspections/InspectionResults.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,4 +566,7 @@ In memoriam, 1972-2018</value>
566566
<data name="InconsistentParamArrayBaseInspection" xml:space="preserve">
567567
<value>ParamArray '{0}' is inconsistently zero-based</value>
568568
</data>
569+
<data name="ParameterlessCellsInspection" xml:space="preserve">
570+
<value>Parameterless 'Range.Cells' call is redundant.</value>
571+
</data>
569572
</root>

RubberduckTests/Inspections/ParameterNotUsedInspectionTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
using System.Linq;
21
using NUnit.Framework;
32
using Rubberduck.CodeAnalysis.Inspections;
43
using Rubberduck.CodeAnalysis.Inspections.Concrete;
54
using Rubberduck.Parsing.VBA;
65
using Rubberduck.VBEditor.SafeComWrappers;
76
using RubberduckTests.Mocks;
7+
using System.Linq;
88

99
namespace RubberduckTests.Inspections
1010
{
@@ -78,12 +78,12 @@ public void ParameterNotUsed_ReturnsResult_SomeParamsUsed()
7878
//See issue #4496 at https://github.com/rubberduck-vba/Rubberduck/issues/4496
7979
public void ParameterNotUsed_RecursiveDefaultMemberAccess_ReturnsNoResult()
8080
{
81-
const string inputCode =
81+
const string inputCode =
8282
@"Public Sub Test(rst As ADODB.Recordset)
8383
Debug.Print rst(""Field"")
8484
End Sub";
8585

86-
var modules = new(string, string, ComponentType)[]
86+
var modules = new (string, string, ComponentType)[]
8787
{
8888
("Module1", inputCode, ComponentType.StandardModule),
8989
};
@@ -104,7 +104,7 @@ public void ParameterNotUsed_InterfaceWithImplementation_ReturnsResultForInterfa
104104
Private Sub IClass1_DoSomething(ByVal a As Integer)
105105
End Sub";
106106

107-
var modules = new(string, string, ComponentType)[]
107+
var modules = new (string, string, ComponentType)[]
108108
{
109109
("IClass1", inputCode1, ComponentType.ClassModule),
110110
("Class1", inputCode2, ComponentType.ClassModule),
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using NUnit.Framework;
2+
using Rubberduck.CodeAnalysis.Inspections;
3+
using Rubberduck.CodeAnalysis.Inspections.Concrete;
4+
using Rubberduck.Parsing.VBA;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
8+
namespace RubberduckTests.Inspections
9+
{
10+
[TestFixture]
11+
public class ParameterlessCellsInspectionTests : InspectionTestsBase
12+
{
13+
[Test]
14+
[Category("Inspections")]
15+
16+
public void ParameterlessCells_ReturnsResult()
17+
{
18+
const string inputCode = @"Option Explicit
19+
Private Sub DoSomething()
20+
Dim Sheet As Worksheet
21+
Set Sheet = ActiveSheet
22+
Debug.Print Sheet.Range(""A1"").Cells.Address
23+
End Sub
24+
";
25+
Assert.AreEqual(1, InspectionResultsFor(inputCode).Count());
26+
}
27+
28+
[Test]
29+
[Category("Inspections")]
30+
public void CellsWithEmptyArgsList_ReturnsResult()
31+
{
32+
const string inputCode = @"Option Explicit
33+
Private Sub DoSomething()
34+
Dim Sheet As Worksheet
35+
Set Sheet = ActiveSheet
36+
Debug.Print Sheet.Range(""A1"").Cells().Address
37+
End Sub
38+
";
39+
Assert.AreEqual(1, InspectionResultsFor(inputCode).Count());
40+
}
41+
42+
[Test]
43+
[Category("Inspections")]
44+
public void CellsWithRowIndexArgument_NoResult()
45+
{
46+
const string inputCode = @"Option Explicit
47+
Private Sub DoSomething()
48+
Dim Sheet As Worksheet
49+
Set Sheet = ActiveSheet
50+
Debug.Print Sheet.Range(""A1"").Cells(42).Address
51+
End Sub
52+
";
53+
Assert.AreEqual(0, InspectionResultsFor(inputCode).Count());
54+
}
55+
56+
[Test]
57+
[Category("Inspections")]
58+
public void CellsWithNamedRowIndexArgument_NoResult()
59+
{
60+
const string inputCode = @"Option Explicit
61+
Private Sub DoSomething()
62+
Dim Sheet As Worksheet
63+
Set Sheet = ActiveSheet
64+
Debug.Print Sheet.Range(""A1"").Cells(RowIndex:=42).Address
65+
End Sub
66+
";
67+
Assert.AreEqual(0, InspectionResultsFor(inputCode).Count());
68+
}
69+
70+
[Test]
71+
[Category("Inspections")]
72+
public void CellsWithNamedColumnIndexArgument_NoResult()
73+
{
74+
const string inputCode = @"Option Explicit
75+
Private Sub DoSomething()
76+
Dim Sheet As Worksheet
77+
Set Sheet = ActiveSheet
78+
Debug.Print Sheet.Range(""A1"").Cells(ColumnIndex:=42).Address
79+
End Sub
80+
";
81+
Assert.AreEqual(0, InspectionResultsFor(inputCode).Count());
82+
}
83+
84+
[Test]
85+
[Category("Inspections")]
86+
public void CellsWithBothArguments_NoResult()
87+
{
88+
const string inputCode = @"Option Explicit
89+
Private Sub DoSomething()
90+
Dim Sheet As Worksheet
91+
Set Sheet = ActiveSheet
92+
Debug.Print Sheet.Range(""A1"").Cells(42, 1).Address
93+
End Sub
94+
";
95+
Assert.AreEqual(0, InspectionResultsFor(inputCode).Count());
96+
}
97+
98+
private IEnumerable<IInspectionResult> InspectionResultsFor(string inputCode) =>
99+
InspectionResultsForModules(
100+
("TestModule1", inputCode, Rubberduck.VBEditor.SafeComWrappers.ComponentType.StandardModule),
101+
new[] { Mocks.ReferenceLibrary.VBA, Mocks.ReferenceLibrary.Excel });
102+
103+
protected override IInspection InspectionUnderTest(RubberduckParserState state)
104+
{
105+
return new ParameterlessCellsInspection(state);
106+
}
107+
}
108+
}

0 commit comments

Comments
 (0)