Skip to content

Commit 663bc50

Browse files
committed
implemented user-defined type member resolution; assignments count as property getter reference, which is a minor bug considering rename refactoring must pick it up anyway, but it does throw off "member not used" inspection results.
1 parent 0004db9 commit 663bc50

File tree

3 files changed

+144
-39
lines changed

3 files changed

+144
-39
lines changed

Rubberduck.Parsing/Symbols/IdentifierReferenceListener.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,18 @@ public override void EnterICS_B_MemberProcedureCall(VBAParser.ICS_B_MemberProced
8484

8585
public override void EnterICS_S_VariableOrProcedureCall(VBAParser.ICS_S_VariableOrProcedureCallContext context)
8686
{
87-
_resolver.Resolve(context);
87+
if (context.Parent.GetType() != typeof (VBAParser.ICS_S_MemberCallContext))
88+
{
89+
_resolver.Resolve(context);
90+
}
8891
}
8992

9093
public override void EnterICS_S_ProcedureOrArrayCall(VBAParser.ICS_S_ProcedureOrArrayCallContext context)
9194
{
92-
_resolver.Resolve(context);
95+
if (context.Parent.GetType() != typeof(VBAParser.ICS_S_MemberCallContext))
96+
{
97+
_resolver.Resolve(context);
98+
}
9399
}
94100

95101
public override void EnterICS_S_MembersCall(VBAParser.ICS_S_MembersCallContext context)
@@ -99,7 +105,10 @@ public override void EnterICS_S_MembersCall(VBAParser.ICS_S_MembersCallContext c
99105

100106
public override void EnterICS_S_DictionaryCall(VBAParser.ICS_S_DictionaryCallContext context)
101107
{
102-
_resolver.Resolve(context);
108+
if (context.Parent.GetType() != typeof(VBAParser.ICS_S_MemberCallContext))
109+
{
110+
_resolver.Resolve(context);
111+
}
103112
}
104113

105114
public override void EnterLetStmt(VBAParser.LetStmtContext context)

Rubberduck.Parsing/Symbols/IdentifierReferenceResolver.cs

Lines changed: 118 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ private Declaration ResolveType(VBAParser.ComplexTypeContext context)
159159
{
160160
return _declarations[identifier.GetText()].SingleOrDefault(item =>
161161
item.ProjectName == libraryName
162-
&& _moduleTypes.Contains(item.DeclarationType));
162+
&& (_moduleTypes.Contains(item.DeclarationType)) || item.DeclarationType == DeclarationType.UserDefinedType);
163163
}
164164

165165
return null;
@@ -194,28 +194,46 @@ private Declaration ResolveType(Declaration parent)
194194
return null;
195195
}
196196

197-
// look in current project first.
198197
var result = _declarations[parent.AsTypeName].SingleOrDefault(item =>
199-
_moduleTypes.Contains(item.DeclarationType)
200-
&& item.ProjectName == _currentScope.ProjectName);
198+
item.DeclarationType == DeclarationType.UserDefinedType
199+
&& item.Project == _currentScope.Project
200+
&& item.ComponentName == _currentScope.ComponentName);
201+
202+
if (result == null)
203+
{
204+
result = _declarations[parent.AsTypeName].SingleOrDefault(item =>
205+
_moduleTypes.Contains(item.DeclarationType)
206+
&& item.Project == _currentScope.Project);
207+
}
201208

202209
if (result == null)
203210
{
204-
// look in all projects (including VbaStdLib library).
205211
result = _declarations[parent.AsTypeName].SingleOrDefault(item =>
206212
_moduleTypes.Contains(item.DeclarationType));
207213
}
208214

209215
return result;
210216
}
211217

218+
private static readonly Type[] IdentifierContexts =
219+
{
220+
typeof (VBAParser.AmbiguousIdentifierContext),
221+
typeof (VBAParser.CertainIdentifierContext)
222+
};
223+
212224
private Declaration ResolveInternal(ParserRuleContext callSiteContext, Declaration localScope, ContextAccessorType accessorType = ContextAccessorType.GetValueOrReference, VBAParser.DictionaryCallStmtContext fieldCall = null, bool hasExplicitLetStatement = false, bool isAssignmentTarget = false)
213225
{
214226
if (callSiteContext == null)
215227
{
216228
return null;
217229
}
218230

231+
if (!IdentifierContexts.Contains(callSiteContext.GetType()))
232+
{
233+
throw new ArgumentException("'" + callSiteContext.GetType().Name + "' is not an identifier context.", "callSiteContext");
234+
}
235+
236+
219237
/* VBA allows ambiguous identifiers; if foo is declared at both
220238
* local and module scope, local scope takes precedence.
221239
* Identifier reference resolution should therefore start search for
@@ -242,10 +260,20 @@ private Declaration ResolveInternal(ParserRuleContext callSiteContext, Declarati
242260
}
243261

244262
var identifierName = callSiteContext.GetText();
245-
var callee = FindLocalScopeDeclaration(identifierName, localScope)
263+
Declaration callee;
264+
if (localScope.DeclarationType == DeclarationType.Variable)
265+
{
266+
// localScope is a UDT
267+
var udt = ResolveType(localScope);
268+
callee = _declarations[identifierName].SingleOrDefault(item => item.Context.Parent == udt.Context);
269+
}
270+
else
271+
{
272+
callee = FindLocalScopeDeclaration(identifierName, localScope)
246273
?? FindModuleScopeDeclaration(identifierName, localScope)
247274
?? FindModuleScopeProcedure(identifierName, localScope, accessorType, isAssignmentTarget)
248275
?? FindProjectScopeDeclaration(identifierName);
276+
}
249277

250278
if (callee == null)
251279
{
@@ -265,7 +293,7 @@ private Declaration ResolveInternal(ParserRuleContext callSiteContext, Declarati
265293

266294
var reference = CreateReference(callSiteContext, callee, isAssignmentTarget, hasExplicitLetStatement);
267295
callee.AddReference(reference);
268-
_alreadyResolved.Add(callSiteContext.Parent);
296+
_alreadyResolved.Add(reference.Context);
269297

270298
if (fieldCall != null)
271299
{
@@ -308,7 +336,7 @@ private Declaration ResolveInternal(VBAParser.DictionaryCallStmtContext fieldCal
308336
var identifierContext = fieldCall.ambiguousIdentifier();
309337
var reference = CreateReference(identifierContext, result, isAssignmentTarget, hasExplicitLetStatement);
310338
result.AddReference(reference);
311-
_alreadyResolved.Add(fieldCall);
339+
_alreadyResolved.Add(reference.Context);
312340

313341
return result;
314342
}
@@ -346,7 +374,7 @@ private Declaration ResolveInternal(VBAParser.ICS_S_MembersCallContext context,
346374
{
347375
var parentReference = CreateReference(parent.Context, parent);
348376
parent.AddReference(parentReference);
349-
_alreadyResolved.Add(parent.Context);
377+
_alreadyResolved.Add(parentReference.Context);
350378
}
351379

352380
var chainedCalls = context.iCS_S_MemberCall();
@@ -404,7 +432,7 @@ private Declaration ResolveInternal(VBAParser.ICS_B_ProcedureCallContext context
404432

405433
var reference = CreateReference(identifierContext, callee);
406434
callee.AddReference(reference);
407-
_alreadyResolved.Add(context);
435+
_alreadyResolved.Add(reference.Context);
408436

409437
return callee;
410438
}
@@ -422,7 +450,9 @@ private void ResolveIdentifier(VBAParser.AmbiguousIdentifierContext context)
422450
return;
423451
}
424452

425-
identifier.AddReference(CreateReference(context, identifier));
453+
var reference = CreateReference(context, identifier);
454+
identifier.AddReference(reference);
455+
_alreadyResolved.Add(reference.Context);
426456
}
427457

428458
public void Resolve(VBAParser.ICS_B_ProcedureCallContext context)
@@ -465,7 +495,7 @@ public void Resolve(VBAParser.ICS_B_MemberProcedureCallContext context)
465495
{
466496
var reference = CreateReference(identifierContext, member);
467497
member.AddReference(reference);
468-
_alreadyResolved.Add(context);
498+
_alreadyResolved.Add(reference.Context);
469499
}
470500

471501
var fieldCall = context.dictionaryCallStmt();
@@ -474,17 +504,54 @@ public void Resolve(VBAParser.ICS_B_MemberProcedureCallContext context)
474504

475505
public void Resolve(VBAParser.ICS_S_VariableOrProcedureCallContext context)
476506
{
477-
TryResolve(context);
507+
ResolveInternal(context, _currentScope);
478508
}
479509

480510
public void Resolve(VBAParser.ICS_S_ProcedureOrArrayCallContext context)
481511
{
482-
TryResolve(context);
512+
ResolveInternal(context, _currentScope);
483513
}
484514

485515
public void Resolve(VBAParser.ICS_S_MembersCallContext context)
486516
{
487-
TryResolve(context);
517+
if (context == null)
518+
{
519+
return;
520+
}
521+
522+
var parent = ResolveInternal(context.iCS_S_ProcedureOrArrayCall(), _currentScope)
523+
?? ResolveInternal(context.iCS_S_VariableOrProcedureCall(), _currentScope);
524+
525+
if (parent != null)
526+
{
527+
var identifierContext = ((dynamic)parent.Context).ambiguousIdentifier() as VBAParser.AmbiguousIdentifierContext;
528+
529+
var parentReference = CreateReference(identifierContext, parent);
530+
parent.AddReference(parentReference);
531+
_alreadyResolved.Add(parentReference.Context);
532+
}
533+
534+
var chainedCalls = context.iCS_S_MemberCall();
535+
foreach (var memberCall in chainedCalls)
536+
{
537+
var member = ResolveInternal(memberCall.iCS_S_ProcedureOrArrayCall(), parent)
538+
?? ResolveInternal(memberCall.iCS_S_VariableOrProcedureCall(), parent);
539+
540+
if (member == null)
541+
{
542+
return;
543+
}
544+
545+
parent = member;
546+
}
547+
548+
var fieldCall = context.dictionaryCallStmt();
549+
if (fieldCall == null)
550+
{
551+
return;
552+
}
553+
554+
ResolveInternal(fieldCall, parent);
488555
}
489556

490557
public void Resolve(VBAParser.ICS_S_DictionaryCallContext context)
@@ -552,6 +619,7 @@ public void Resolve(VBAParser.AsTypeClauseContext context)
552619
if (type != null)
553620
{
554621
type.AddReference(reference);
622+
_alreadyResolved.Add(reference.Context);
555623
}
556624
}
557625

@@ -566,7 +634,8 @@ public void Resolve(VBAParser.ForNextStmtContext context)
566634

567635
if (identifiers.Count > 1)
568636
{
569-
identifier.AddReference(CreateReference(identifiers[1], identifier));
637+
var endForBlockReference = CreateReference(identifiers[1], identifier);
638+
identifier.AddReference(endForBlockReference);
570639
}
571640
}
572641

@@ -631,16 +700,40 @@ private Declaration FindParentCall(VBAParser.VsAssignContext context)
631700
?? ResolveInternal(calleeContext as VBAParser.ICS_S_MembersCallContext, _currentScope);
632701
}
633702

703+
private Declaration FindFunctionOrPropertyGetter(string identifierName, Declaration localScope = null)
704+
{
705+
if (localScope == null)
706+
{
707+
localScope = _currentScope;
708+
}
709+
710+
var matches = _declarations[identifierName];
711+
var parent = matches.SingleOrDefault(item =>
712+
item.Scope == localScope.Scope
713+
&& (item.DeclarationType == DeclarationType.Function
714+
|| item.DeclarationType == DeclarationType.PropertyGet));
715+
716+
return parent;
717+
}
718+
634719
private Declaration FindLocalScopeDeclaration(string identifierName, Declaration localScope = null)
635720
{
636721
if (localScope == null)
637722
{
638723
localScope = _currentScope;
639724
}
640725

641-
var parent = _declarations[identifierName].SingleOrDefault(item =>
726+
if (localScope.DeclarationType == DeclarationType.Function ||
727+
localScope.DeclarationType == DeclarationType.PropertyGet)
728+
{
729+
return FindFunctionOrPropertyGetter(identifierName, localScope);
730+
}
731+
732+
var matches = _declarations[identifierName];
733+
var parent = matches.SingleOrDefault(item =>
642734
item.ParentScope == localScope.Scope
643735
&& !_moduleTypes.Contains(item.DeclarationType));
736+
644737
return parent;
645738
}
646739

@@ -651,7 +744,8 @@ private Declaration FindModuleScopeDeclaration(string identifierName, Declaratio
651744
localScope = _currentScope;
652745
}
653746

654-
return _declarations[identifierName].SingleOrDefault(item =>
747+
var matches = _declarations[identifierName];
748+
return matches.SingleOrDefault(item =>
655749
item.ParentScope == localScope.ParentScope
656750
&& !item.DeclarationType.HasFlag(DeclarationType.Member)
657751
&& !_moduleTypes.Contains(item.DeclarationType));
@@ -664,17 +758,19 @@ private Declaration FindModuleScopeProcedure(string identifierName, Declaration
664758
localScope = _currentScope;
665759
}
666760

667-
return _declarations[identifierName].SingleOrDefault(item =>
761+
var matches = _declarations[identifierName];
762+
return matches.SingleOrDefault(item =>
668763
IsProcedure(item) || IsPropertyAccessor(item, accessorType, localScope, isAssignmentTarget));
669764
}
670765

671766
private Declaration FindProjectScopeDeclaration(string identifierName)
672767
{
673768
// assume unqualified variable call, i.e. unique declaration.
674-
return _declarations[identifierName].SingleOrDefault(item =>
675-
(item.Accessibility == Accessibility.Public
769+
return _declarations[identifierName].SingleOrDefault(item =>
770+
!item.DeclarationType.HasFlag(DeclarationType.Member)
771+
&& (item.Accessibility == Accessibility.Public
676772
|| item.Accessibility == Accessibility.Global
677-
|| _moduleTypes.Contains(item.DeclarationType)));
773+
|| _moduleTypes.Contains(item.DeclarationType) /* because static classes are accessed just like modules */));
678774
}
679775

680776
private bool IsProcedure(Declaration item)

Rubberduck.Parsing/VBProjectParseResult.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,20 @@ private void OnProgress(VBComponentParseResult result)
4747
public void Resolve()
4848
{
4949
// make a first pass to identify all interface implementations - resolver needs this to disembiguate members.
50-
foreach (var componentParseResult in _parseResults)
51-
{
52-
try
53-
{
54-
var resolver = new IdentifierReferenceResolver(componentParseResult.QualifiedName, _declarations);
55-
var listener = new InterfaceImplementationListener(resolver);
56-
var walker = new ParseTreeWalker();
57-
walker.Walk(listener, componentParseResult.ParseTree);
58-
}
59-
catch (WalkerCancelledException)
60-
{
61-
// exception is purposely thrown when walker exits the module's declarations section.
62-
}
63-
}
50+
//foreach (var componentParseResult in _parseResults)
51+
//{
52+
// try
53+
// {
54+
// var resolver = new IdentifierReferenceResolver(componentParseResult.QualifiedName, _declarations);
55+
// var listener = new InterfaceImplementationListener(resolver);
56+
// var walker = new ParseTreeWalker();
57+
// walker.Walk(listener, componentParseResult.ParseTree);
58+
// }
59+
// catch (WalkerCancelledException)
60+
// {
61+
// // exception is purposely thrown when walker exits the module's declarations section.
62+
// }
63+
//}
6464

6565
// second pass; resolve all identifier usages
6666
foreach (var componentParseResult in _parseResults)

0 commit comments

Comments
 (0)