Skip to content

Commit a8286a9

Browse files
committed
patchwork fix for an undocumented bug with IdentifierReferenceListener where references of procedure 'Foo' wouldn't get picked up correctly if more than 1 module defined a Foo' member.
1 parent d78d70d commit a8286a9

File tree

2 files changed

+37
-12
lines changed

2 files changed

+37
-12
lines changed

RetailCoder.VBE/Inspections/ProcedureNotUsedInspection.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ public ProcedureNotUsedInspection()
2020
public IEnumerable<CodeInspectionResultBase> GetInspectionResults(VBProjectParseResult parseResult)
2121
{
2222
var handlers = parseResult.Declarations.Items.Where(item => item.DeclarationType == DeclarationType.Control)
23-
.SelectMany(control => parseResult.Declarations.FindEventHandlers(control));
23+
.SelectMany(control => parseResult.Declarations.FindEventHandlers(control))
24+
.ToList();
2425

2526
var issues = parseResult.Declarations.Items
26-
.Where(item => !IsIgnoredProcedure(parseResult.Declarations, item, handlers))
27-
.Select(issue => new IdentifierNotUsedInspectionResult(string.Format(Name, issue.IdentifierName), Severity, issue.Context, issue.QualifiedName.QualifiedModuleName));
27+
.Where(item => !IsIgnoredDeclaration(parseResult.Declarations, item, handlers))
28+
.Select(issue => new IdentifierNotUsedInspectionResult(string.Format(Name, issue.IdentifierName), Severity, issue.Context, issue.QualifiedName.QualifiedModuleName))
29+
.ToList();
2830

2931
return issues;
3032
}
@@ -35,7 +37,7 @@ public IEnumerable<CodeInspectionResultBase> GetInspectionResults(VBProjectParse
3537
DeclarationType.Function
3638
};
3739

38-
private bool IsIgnoredProcedure(Declarations declarations, Declaration declaration, IEnumerable<Declaration> handlers)
40+
private bool IsIgnoredDeclaration(Declarations declarations, Declaration declaration, IEnumerable<Declaration> handlers)
3941
{
4042
var result = !ProcedureTypes.Contains(declaration.DeclarationType)
4143
|| declaration.References.Any()
@@ -106,7 +108,6 @@ private bool IsInterfaceMember(Declarations declarations, Declaration procedure)
106108
return true;
107109
}
108110

109-
// todo: find a way to avoid running this for every procedure in a class
110111
var result = GetImplementedInterfaceMembers(declarations, procedure.ComponentName)
111112
.Contains(procedure.IdentifierName);
112113

Rubberduck.Parsing/Symbols/IdentifierReferenceListener.cs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ private bool EnterIdentifier(ParserRuleContext context, Selection selection, boo
330330
var name = context.GetText();
331331
var matches = _declarations[name].Where(IsInScope);
332332

333-
var declaration = GetClosestScope(matches);
333+
var declaration = GetClosestScope(matches, context);
334334
if (declaration != null)
335335
{
336336
var reference = new IdentifierReference(_qualifiedName, name, selection, context, declaration, isAssignmentTarget, hasExplicitLetStatement);
@@ -357,24 +357,29 @@ public override void EnterVsAssign(VBAParser.VsAssignContext context)
357357
var callStatementD = context.Parent.Parent.Parent as VBAParser.ICS_B_ProcedureCallContext;
358358

359359
var procedureName = string.Empty;
360+
ParserRuleContext identifierContext = null;
360361
if (callStatementA != null)
361362
{
362363
procedureName = callStatementA.ambiguousIdentifier().GetText();
364+
identifierContext = callStatementA.ambiguousIdentifier();
363365
}
364366
else if(callStatementB != null)
365367
{
366368
procedureName = callStatementB.ambiguousIdentifier().GetText();
369+
identifierContext = callStatementB.ambiguousIdentifier();
367370
}
368371
else if (callStatementC != null)
369372
{
370373
procedureName = callStatementC.ambiguousIdentifier().GetText();
374+
identifierContext = callStatementC.ambiguousIdentifier();
371375
}
372376
else if (callStatementD != null)
373377
{
374378
procedureName = callStatementD.certainIdentifier().GetText();
379+
identifierContext = callStatementD.certainIdentifier();
375380
}
376381

377-
var procedure = FindProcedureDeclaration(procedureName);
382+
var procedure = FindProcedureDeclaration(procedureName, identifierContext);
378383
if (procedure == null)
379384
{
380385
return;
@@ -393,13 +398,13 @@ public override void EnterVsAssign(VBAParser.VsAssignContext context)
393398
}
394399
}
395400

396-
private Declaration FindProcedureDeclaration(string procedureName)
401+
private Declaration FindProcedureDeclaration(string procedureName, ParserRuleContext context)
397402
{
398403
var matches = _declarations[procedureName]
399404
.Where(declaration => ProcedureDeclarations.Contains(declaration.DeclarationType))
400405
.Where(IsInScope);
401406

402-
var procedure = GetClosestScope(matches);
407+
var procedure = GetClosestScope(matches, context);
403408
return procedure;
404409
}
405410

@@ -428,7 +433,8 @@ private bool IsInScope(Declaration declaration)
428433

429434
if (ProcedureDeclarations.Contains(declaration.DeclarationType))
430435
{
431-
if (declaration.Accessibility == Accessibility.Public)
436+
if (declaration.Accessibility == Accessibility.Public
437+
|| declaration.Accessibility == Accessibility.Implicit)
432438
{
433439
var result = declaration.Project.Equals(_qualifiedName.Project);
434440
return result;
@@ -443,7 +449,7 @@ private bool IsInScope(Declaration declaration)
443449
|| IsGlobalProcedure(declaration);
444450
}
445451

446-
private Declaration GetClosestScope(IEnumerable<Declaration> declarations)
452+
private Declaration GetClosestScope(IEnumerable<Declaration> declarations, ParserRuleContext context)
447453
{
448454
// this method (as does the rest of Rubberduck) assumes the VBA code is compilable.
449455

@@ -460,7 +466,25 @@ private Declaration GetClosestScope(IEnumerable<Declaration> declarations)
460466
return moduleScope;
461467
}
462468

463-
// multiple matches in global scope??
469+
if (matches.Count == 1)
470+
{
471+
return matches[0];
472+
}
473+
474+
var memberProcedureCallContext = context.Parent as VBAParser.ICS_B_MemberProcedureCallContext;
475+
if (memberProcedureCallContext != null)
476+
{
477+
var parentMemberName = memberProcedureCallContext.implicitCallStmt_InStmt().Stop.Text;
478+
var matchingParents = _declarations.Items.Where(d => d.IdentifierName == parentMemberName);
479+
480+
var matchingParent = matchingParents.FirstOrDefault();
481+
if (matchingParent != null)
482+
{
483+
var parentType = matches.FirstOrDefault(p => p.ComponentName == matchingParent.AsTypeName);
484+
return matches.FirstOrDefault(m => m.ParentScope == parentType.ParentScope);
485+
}
486+
}
487+
464488
return matches.FirstOrDefault();
465489
}
466490

0 commit comments

Comments
 (0)