Skip to content

Commit f929c29

Browse files
committed
IdentifierReference chained member calls now exit early; if Foo isn't a known identifier in Foo.Bar.Baz, code will no longer try to find the declaration for Bar and Baz. This fixes a bug with identifier usages, where "ADODB.Connection" was picked up as a usage of parameter "connection" although ADODB wasn't a known identifier.
1 parent b47a864 commit f929c29

File tree

1 file changed

+59
-10
lines changed

1 file changed

+59
-10
lines changed

Rubberduck.Parsing/Symbols/IdentifierReferenceListener.cs

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ private VBAParser.AmbiguousIdentifierContext FindAssignmentTarget(VBAParser.Impl
196196
{
197197
EnterIdentifier(reference, reference.GetSelection());
198198
}
199+
else
200+
{
201+
break;
202+
}
199203
}
200204

201205
var varOrProcCall = member.iCS_S_VariableOrProcedureCall();
@@ -208,28 +212,48 @@ private VBAParser.AmbiguousIdentifierContext FindAssignmentTarget(VBAParser.Impl
208212
{
209213
EnterIdentifier(reference, reference.GetSelection());
210214
}
215+
else
216+
{
217+
break;
218+
}
211219
}
212220
}
213221
else
214222
{
215223
var procOrArrayCall = member.iCS_S_ProcedureOrArrayCall();
216224
if (procOrArrayCall != null)
217225
{
218-
return EnterDictionaryCall(procOrArrayCall.dictionaryCallStmt(), procOrArrayCall.ambiguousIdentifier())
219-
?? procOrArrayCall.ambiguousIdentifier();
226+
var identifier = procOrArrayCall.ambiguousIdentifier();
227+
if (_declarations.Items.Any(item => item.IdentifierName == identifier.GetText()))
228+
{
229+
return EnterDictionaryCall(procOrArrayCall.dictionaryCallStmt(), identifier)
230+
?? identifier;
231+
}
232+
}
233+
else
234+
{
235+
break;
220236
}
221237

222238
var varOrProcCall = member.iCS_S_VariableOrProcedureCall();
223239
if (varOrProcCall != null)
224240
{
225-
return EnterDictionaryCall(varOrProcCall.dictionaryCallStmt(), varOrProcCall.ambiguousIdentifier())
226-
?? varOrProcCall.ambiguousIdentifier();
241+
var identifier = varOrProcCall.ambiguousIdentifier();
242+
if (_declarations.Items.Any(item => item.IdentifierName == identifier.GetText()))
243+
{
244+
return EnterDictionaryCall(varOrProcCall.dictionaryCallStmt(), identifier)
245+
?? identifier;
246+
}
247+
}
248+
else
249+
{
250+
break;
227251
}
228252
}
229253
}
230254
}
231255

232-
return null; // not possible unless grammar is modified.
256+
return null;
233257
}
234258

235259
private VBAParser.AmbiguousIdentifierContext EnterDictionaryCall(VBAParser.DictionaryCallStmtContext dictionaryCall, VBAParser.AmbiguousIdentifierContext parentIdentifier = null)
@@ -241,15 +265,37 @@ private VBAParser.AmbiguousIdentifierContext EnterDictionaryCall(VBAParser.Dicti
241265

242266
if (parentIdentifier != null)
243267
{
244-
EnterIdentifier(parentIdentifier, parentIdentifier.GetSelection()); // we're referencing "member" in "member!field"
268+
if (!EnterIdentifier(parentIdentifier, parentIdentifier.GetSelection()))
269+
// we're referencing "member" in "member!field"
270+
{
271+
return null;
272+
}
245273
}
246-
247-
return dictionaryCall.ambiguousIdentifier();
274+
275+
var identifier = dictionaryCall.ambiguousIdentifier();
276+
if (_declarations.Items.Any(item => item.IdentifierName == identifier.GetText()))
277+
{
278+
return identifier;
279+
}
280+
281+
return null;
248282
}
249283

284+
public override void EnterComplexType(VBAParser.ComplexTypeContext context)
285+
{
286+
var identifiers = context.ambiguousIdentifier();
287+
_skipIdentifiers = !identifiers.All(identifier => _declarations.Items.Any(declaration => declaration.IdentifierName == identifier.GetText()));
288+
}
289+
290+
public override void ExitComplexType(VBAParser.ComplexTypeContext context)
291+
{
292+
_skipIdentifiers = false;
293+
}
294+
295+
private bool _skipIdentifiers;
250296
public override void EnterAmbiguousIdentifier(VBAParser.AmbiguousIdentifierContext context)
251297
{
252-
if (IsDeclarativeContext(context))
298+
if (_skipIdentifiers || IsDeclarativeContext(context))
253299
{
254300
return;
255301
}
@@ -279,7 +325,7 @@ public override void EnterCertainIdentifier(VBAParser.CertainIdentifierContext c
279325
EnterIdentifier(context, selection);
280326
}
281327

282-
private void EnterIdentifier(ParserRuleContext context, Selection selection, bool isAssignmentTarget = false, bool hasExplicitLetStatement = false)
328+
private bool EnterIdentifier(ParserRuleContext context, Selection selection, bool isAssignmentTarget = false, bool hasExplicitLetStatement = false)
283329
{
284330
var name = context.GetText();
285331
var matches = _declarations[name].Where(IsInScope);
@@ -292,9 +338,12 @@ private void EnterIdentifier(ParserRuleContext context, Selection selection, boo
292338
if (!declaration.References.Select(r => r.Context).Contains(reference.Context))
293339
{
294340
declaration.AddReference(reference);
341+
return true;
295342
}
296343
// note: non-matching names are not necessarily undeclared identifiers, e.g. "String" in "Dim foo As String".
297344
}
345+
346+
return false;
298347
}
299348

300349
public override void EnterVsAssign(VBAParser.VsAssignContext context)

0 commit comments

Comments
 (0)