Skip to content

Commit c0b51aa

Browse files
committed
Add member access may be Nothing inspection.
1 parent 1af96a9 commit c0b51aa

File tree

3 files changed

+429
-0
lines changed

3 files changed

+429
-0
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Antlr4.Runtime.Tree;
4+
using Rubberduck.Inspections.CodePathAnalysis;
5+
using Rubberduck.Inspections.CodePathAnalysis.Nodes;
6+
using Rubberduck.Inspections.Results;
7+
using Rubberduck.Parsing;
8+
using Rubberduck.Parsing.Grammar;
9+
using Rubberduck.Parsing.Inspections.Abstract;
10+
using Rubberduck.Parsing.Symbols;
11+
using Rubberduck.Parsing.VBA;
12+
13+
namespace Rubberduck.Inspections.Abstract
14+
{
15+
public abstract class MemberAccessMayReturnNothingInspectionBase : InspectionBase
16+
{
17+
protected MemberAccessMayReturnNothingInspectionBase(RubberduckParserState state) : base(state) { }
18+
19+
public abstract List<Declaration> MembersUnderTest { get; }
20+
public abstract string ResultTemplate { get; }
21+
22+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
23+
{
24+
var interesting = MembersUnderTest.SelectMany(member => member.References).ToList();
25+
if (!interesting.Any())
26+
{
27+
return Enumerable.Empty<IInspectionResult>();
28+
}
29+
30+
var output = new List<IInspectionResult>();
31+
foreach (var reference in interesting)
32+
{
33+
var access = reference.Context.GetAncestor<VBAParser.MemberAccessExprContext>();
34+
var usageContext = access.Parent is VBAParser.IndexExprContext
35+
? access.Parent.Parent
36+
: access.Parent;
37+
38+
var setter = usageContext is VBAParser.LExprContext lexpr && lexpr.Parent is VBAParser.SetStmtContext
39+
? lexpr.Parent
40+
: null;
41+
42+
if (setter is null)
43+
{
44+
if (usageContext is VBAParser.MemberAccessExprContext || !ContextIsNothingTest(usageContext))
45+
{
46+
output.Add(new IdentifierReferenceInspectionResult(this,
47+
string.Format(ResultTemplate,
48+
$"{reference.Declaration.ParentDeclaration.IdentifierName}.{reference.IdentifierName}"),
49+
State, reference));
50+
}
51+
continue;
52+
}
53+
54+
var assignedTo = Declarations.SelectMany(decl => decl.References).SingleOrDefault(assign =>
55+
assign.IsAssignment && (assign.Context.GetAncestor<VBAParser.SetStmtContext>()?.Equals(setter) ?? false));
56+
if (assignedTo is null)
57+
{
58+
continue;
59+
}
60+
61+
var tree = new Walker().GenerateTree(assignedTo.Declaration.ParentScopeDeclaration.Context, assignedTo.Declaration);
62+
var firstUse = GetReferenceNodes(tree).FirstOrDefault();
63+
if (firstUse is null || ContextIsNothingTest(firstUse.Reference.Context.Parent))
64+
{
65+
continue;
66+
}
67+
68+
output.Add(new IdentifierReferenceInspectionResult(this,
69+
string.Format(ResultTemplate,
70+
$"{reference.Declaration.ParentDeclaration.IdentifierName}.{reference.IdentifierName}"),
71+
State, reference));
72+
}
73+
74+
return output;
75+
}
76+
77+
private bool ContextIsNothingTest(IParseTree context)
78+
{
79+
return context is VBAParser.LExprContext &&
80+
context.Parent is VBAParser.RelationalOpContext comparison &&
81+
comparison.IS() != null
82+
&& comparison.GetDescendent<VBAParser.ObjectLiteralIdentifierContext>() != null;
83+
}
84+
85+
private IEnumerable<INode> GetReferenceNodes(INode node)
86+
{
87+
if (node is ReferenceNode && node.Reference != null)
88+
{
89+
yield return node;
90+
}
91+
92+
foreach (var child in node.Children)
93+
{
94+
foreach (var childNode in GetReferenceNodes(child))
95+
{
96+
yield return childNode;
97+
}
98+
}
99+
}
100+
}
101+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Rubberduck.Inspections.Abstract;
5+
using Rubberduck.Parsing.Inspections;
6+
using Rubberduck.Parsing.Symbols;
7+
using Rubberduck.Parsing.VBA;
8+
using Rubberduck.Resources.Inspections;
9+
10+
namespace Rubberduck.Inspections.Concrete
11+
{
12+
[RequiredLibrary("Excel")]
13+
public class ExcelMemberMayReturnNothingInspection : MemberAccessMayReturnNothingInspectionBase
14+
{
15+
public ExcelMemberMayReturnNothingInspection(RubberduckParserState state) : base(state) { }
16+
17+
private static readonly List<string> ExcelMembers = new List<string>
18+
{
19+
"Range.Find",
20+
"Range.FindNext",
21+
"Range.FindPrevious"
22+
};
23+
24+
public override List<Declaration> MembersUnderTest => BuiltInDeclarations
25+
.Where(decl => decl.ProjectName.Equals("Excel") && ExcelMembers.Any(member => decl.QualifiedName.ToString().EndsWith(member)))
26+
.ToList();
27+
28+
public override string ResultTemplate => Description; //InspectionResults.ExcelMemberMayReturnNothingInspection;
29+
}
30+
}

0 commit comments

Comments
 (0)