Skip to content

Commit 8cb1a2d

Browse files
committed
Extend FindParameterFromArgument to non-procedure non default members
This also changes the name to voice all the shortcomings of this method.
1 parent 64ae145 commit 8cb1a2d

File tree

5 files changed

+102
-35
lines changed

5 files changed

+102
-35
lines changed

Rubberduck.CodeAnalysis/Inspections/Concrete/NonReturningFunctionInspection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
5151
private bool IsAssignedByRefArgument(Declaration enclosingProcedure, IdentifierReference reference)
5252
{
5353
var argExpression = reference.Context.GetAncestor<VBAParser.ArgumentExpressionContext>();
54-
var parameter = State.DeclarationFinder.FindParameterFromArgument(argExpression, enclosingProcedure);
54+
var parameter = State.DeclarationFinder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, enclosingProcedure);
5555

5656
// note: not recursive, by design.
5757
return parameter != null

Rubberduck.CodeAnalysis/Inspections/Concrete/UnassignedVariableUsageInspection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
5454
private bool IsAssignedByRefArgument(Declaration enclosingProcedure, IdentifierReference reference)
5555
{
5656
var argExpression = reference.Context.GetAncestor<VBAParser.ArgumentExpressionContext>();
57-
var parameter = State.DeclarationFinder.FindParameterFromArgument(argExpression, enclosingProcedure);
57+
var parameter = State.DeclarationFinder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, enclosingProcedure);
5858

5959
// note: not recursive, by design.
6060
return parameter != null

Rubberduck.CodeAnalysis/Inspections/Concrete/VariableNotAssignedInspection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
3737
private bool IsAssignedByRefArgument(Declaration enclosingProcedure, IdentifierReference reference)
3838
{
3939
var argExpression = reference.Context.GetAncestor<VBAParser.ArgumentExpressionContext>();
40-
var parameter = State.DeclarationFinder.FindParameterFromArgument(argExpression, enclosingProcedure);
40+
var parameter = State.DeclarationFinder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, enclosingProcedure);
4141

4242
// note: not recursive, by design.
4343
return parameter != null

Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs

Lines changed: 98 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Diagnostics;
55
using System.Linq;
6+
using System.Runtime.InteropServices.WindowsRuntime;
67
using Antlr4.Runtime;
78
using NLog;
89
using Rubberduck.Parsing.Annotations;
@@ -542,46 +543,34 @@ public IEnumerable<Declaration> MatchName(string name)
542543
: Enumerable.Empty<Declaration>();
543544
}
544545

545-
public ParameterDeclaration FindParameterFromArgument(VBAParser.ArgumentExpressionContext argExpression, Declaration enclosingProcedure)
546+
public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(VBAParser.ArgumentExpressionContext argumentExpression, Declaration enclosingProcedure)
546547
{
547-
if (argExpression == null ||
548-
argExpression.GetDescendent<VBAParser.ParenthesizedExprContext>() != null ||
549-
argExpression.BYVAL() != null)
550-
{
551-
// not an argument, or argument is parenthesized and thus passed ByVal
552-
return null;
553-
}
554-
555-
var callStmt = argExpression.GetAncestor<VBAParser.CallStmtContext>();
556-
557-
var identifier = callStmt?
558-
.GetDescendent<VBAParser.LExpressionContext>()
559-
.GetDescendents<VBAParser.IdentifierContext>()
560-
.LastOrDefault();
548+
return FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argumentExpression, enclosingProcedure.QualifiedModuleName);
549+
}
561550

562-
if (identifier == null)
563-
{
564-
// if we don't know what we're calling, we can't dig any further
565-
return null;
566-
}
551+
public ParameterDeclaration FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(VBAParser.ArgumentExpressionContext argumentExpression, QualifiedModuleName module)
552+
{
553+
//todo: Rename after making it work for more general cases.
567554

568-
var selection = new QualifiedSelection(enclosingProcedure.QualifiedModuleName, identifier.GetSelection());
569-
if (!_referencesBySelection.TryGetValue(selection, out var matches))
555+
if (argumentExpression == null
556+
|| argumentExpression.GetDescendent<VBAParser.ParenthesizedExprContext>() != null
557+
|| argumentExpression.BYVAL() != null)
570558
{
559+
// not a simple argument, or argument is parenthesized and thus passed ByVal
571560
return null;
572561
}
573562

574-
var procedure = matches.SingleOrDefault()?.Declaration;
575-
if (procedure?.ParentScopeDeclaration is ClassModuleDeclaration)
563+
var callingNonDefaultMember = CallingNonDefaultMember(argumentExpression, module);
564+
if (callingNonDefaultMember == null)
576565
{
577-
// we can't know that the member is on the class' default interface
566+
// Either we could not resolve the call or there is a default member call involved.
578567
return null;
579568
}
580569

581-
var parameters = Parameters(procedure);
570+
var parameters = Parameters(callingNonDefaultMember);
582571

583572
ParameterDeclaration parameter;
584-
var namedArg = argExpression.GetAncestor<VBAParser.NamedArgumentContext>();
573+
var namedArg = argumentExpression.GetAncestor<VBAParser.NamedArgumentContext>();
585574
if (namedArg != null)
586575
{
587576
// argument is named: we're lucky
@@ -591,11 +580,11 @@ public ParameterDeclaration FindParameterFromArgument(VBAParser.ArgumentExpressi
591580
else
592581
{
593582
// argument is positional: work out its index
594-
var argList = callStmt.GetDescendent<VBAParser.ArgumentListContext>();
595-
var args = argList.GetDescendents<VBAParser.PositionalArgumentContext>().ToArray();
583+
var argumentList = argumentExpression.GetAncestor<VBAParser.ArgumentListContext>();
584+
var arguments = argumentList.GetDescendents<VBAParser.PositionalArgumentContext>().ToArray();
596585

597-
var parameterIndex = args
598-
.Select((param, index) => param.GetDescendent<VBAParser.ArgumentExpressionContext>() == argExpression ? (param, index) : (null, -1))
586+
var parameterIndex = arguments
587+
.Select((param, index) => param.GetDescendent<VBAParser.ArgumentExpressionContext>() == argumentExpression ? (param, index) : (null, -1))
599588
.SingleOrDefault(item => item.param != null).index;
600589

601590
parameter = parameters
@@ -607,6 +596,74 @@ public ParameterDeclaration FindParameterFromArgument(VBAParser.ArgumentExpressi
607596
return parameter;
608597
}
609598

599+
private ModuleBodyElementDeclaration CallingNonDefaultMember(VBAParser.ArgumentExpressionContext argumentExpression, QualifiedModuleName module)
600+
{
601+
//todo: Make this work for default member calls.
602+
603+
var argumentList = argumentExpression.GetAncestor<VBAParser.ArgumentListContext>();
604+
var cannotHaveDefaultMemberCall = false;
605+
606+
ParserRuleContext callingExpression;
607+
switch (argumentList.Parent)
608+
{
609+
case VBAParser.CallStmtContext callStmt:
610+
cannotHaveDefaultMemberCall = true;
611+
callingExpression = callStmt.lExpression();
612+
break;
613+
case VBAParser.IndexExprContext indexExpr:
614+
callingExpression = indexExpr.lExpression();
615+
break;
616+
case VBAParser.WhitespaceIndexExprContext indexExpr:
617+
callingExpression = indexExpr.lExpression();
618+
break;
619+
default:
620+
//This should never happen.
621+
return null;
622+
}
623+
624+
VBAParser.IdentifierContext callingIdentifier;
625+
if (cannotHaveDefaultMemberCall)
626+
{
627+
callingIdentifier = callingExpression
628+
.GetDescendents<VBAParser.IdentifierContext>()
629+
.LastOrDefault();
630+
}
631+
else
632+
{
633+
switch (callingExpression)
634+
{
635+
case VBAParser.SimpleNameExprContext simpleName:
636+
callingIdentifier = simpleName.identifier();
637+
break;
638+
case VBAParser.MemberAccessExprContext memberAccess:
639+
callingIdentifier = memberAccess
640+
.GetDescendents<VBAParser.IdentifierContext>()
641+
.LastOrDefault();
642+
break;
643+
case VBAParser.WithMemberAccessExprContext memberAccess:
644+
callingIdentifier = memberAccess
645+
.GetDescendents<VBAParser.IdentifierContext>()
646+
.LastOrDefault();
647+
break;
648+
default:
649+
//This is only possible in case of a default member access.
650+
return null;
651+
}
652+
}
653+
654+
if (callingIdentifier == null)
655+
{
656+
return null;
657+
}
658+
659+
var referencedMember = IdentifierReferences(callingIdentifier, module)
660+
.Select(reference => reference.Declaration)
661+
.OfType<ModuleBodyElementDeclaration>()
662+
.FirstOrDefault();
663+
664+
return referencedMember;
665+
}
666+
610667
private string ToNormalizedName(string name)
611668
{
612669
var lower = name.ToLowerInvariant();
@@ -1253,6 +1310,16 @@ public IEnumerable<IdentifierReference> IdentifierReferences(QualifiedModuleName
12531310
: Enumerable.Empty<IdentifierReference>();
12541311
}
12551312

1313+
/// <summary>
1314+
/// Gets all identifier references for an IdentifierContext.
1315+
/// </summary>
1316+
public IEnumerable<IdentifierReference> IdentifierReferences(VBAParser.IdentifierContext identifierContext, QualifiedModuleName module)
1317+
{
1318+
var qualifiedSelection = new QualifiedSelection(module, identifierContext.GetSelection());
1319+
return IdentifierReferences(qualifiedSelection)
1320+
.Where(identifierReference => identifierReference.IdentifierName.Equals(identifierContext.GetText()));
1321+
}
1322+
12561323
/// <summary>
12571324
/// Gets all identifier references with the specified selection.
12581325
/// </summary>

RubberduckTests/Symbols/DeclarationFinderTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1245,7 +1245,7 @@ End Sub
12451245

12461246
var enclosing = declarations.FirstOrDefault(decl => decl.IdentifierName.Equals("Bar"));
12471247
var context = enclosing?.Context.GetDescendent<VBAParser.ArgumentExpressionContext>();
1248-
var actual = state.DeclarationFinder.FindParameterFromArgument(context, enclosing);
1248+
var actual = state.DeclarationFinder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(context, enclosing);
12491249

12501250
Assert.AreEqual(expected, actual);
12511251
}

0 commit comments

Comments
 (0)