Skip to content

Commit 4373608

Browse files
committed
Collect failed indexed default member resolutions
This also fixes various small bugs in the reference resolver. Moreover, now we actually check whether the number or arguments provided fits for index expressions.
1 parent 14f957b commit 4373608

File tree

7 files changed

+501
-21
lines changed

7 files changed

+501
-21
lines changed

Rubberduck.Parsing/Binding/Bindings/DictionaryAccessDefaultBinding.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ private static IBoundExpression CreateFailedExpression(IBoundExpression lExpress
108108
return failedExpr.JoinAsFailedResolution(context, argumentExpressions);
109109
}
110110

111+
private static IBoundExpression CreateFailedDefaultMemberAccessExpression(IBoundExpression lExpression, ArgumentList argumentList, ParserRuleContext context)
112+
{
113+
var failedExpr = new DictionaryAccessExpression(lExpression.ReferencedDeclaration, ExpressionClassification.ResolutionFailed, context, lExpression, argumentList, context);
114+
115+
var argumentExpressions = argumentList.Arguments.Select(arg => arg.Expression);
116+
return failedExpr.JoinAsFailedResolution(context, argumentExpressions.Concat(new []{ lExpression }));
117+
}
118+
111119
private static IBoundExpression ResolveViaDefaultMember(IBoundExpression lExpression, string asTypeName, Declaration asTypeDeclaration, ArgumentList argumentList, ParserRuleContext expression, ParserRuleContext defaultMemberContext, int recursionDepth = 1, RecursiveDefaultMemberAccessExpression containedExpression = null)
112120
{
113121
if (Tokens.Variant.Equals(asTypeName, StringComparison.InvariantCultureIgnoreCase)
@@ -132,7 +140,7 @@ The declared type of <l-expression> is Object or Variant.
132140
|| !IsPublic(defaultMember))
133141
{
134142
ResolveArgumentList(null, argumentList);
135-
return CreateFailedExpression(lExpression, argumentList, expression);
143+
return CreateFailedDefaultMemberAccessExpression(lExpression, argumentList, expression);
136144
}
137145

138146
var defaultMemberClassification = DefaultMemberClassification(defaultMember);
@@ -147,7 +155,7 @@ The declared type of <l-expression> is Object or Variant.
147155
declared type.
148156
*/
149157
ResolveArgumentList(defaultMember, argumentList);
150-
return new DictionaryAccessExpression(defaultMember, defaultMemberClassification, expression, lExpression, argumentList, defaultMemberContext, recursionDepth, containedExpression);
158+
return new DictionaryAccessExpression(defaultMember, ExpressionClassification.Variable, expression, lExpression, argumentList, defaultMemberContext, recursionDepth, containedExpression);
151159
}
152160

153161
if (parameters.All(param => param.IsOptional)
@@ -162,13 +170,13 @@ declared type.
162170
}
163171

164172
ResolveArgumentList(null, argumentList);
165-
return CreateFailedExpression(lExpression, argumentList, expression);
173+
return CreateFailedDefaultMemberAccessExpression(lExpression, argumentList, expression);
166174
}
167175

168176
private static bool IsCompatibleWithOneStringArgument(List<ParameterDeclaration> parameters)
169177
{
170178
return parameters.Count > 0
171-
&& parameters.Count(param => !param.IsOptional) <= 1
179+
&& parameters.Count(param => !param.IsOptional && !param.IsParamArray) <= 1
172180
&& (Tokens.String.Equals(parameters[0].AsTypeName, StringComparison.InvariantCultureIgnoreCase)
173181
|| Tokens.Variant.Equals(parameters[0].AsTypeName, StringComparison.InvariantCultureIgnoreCase));
174182
}

Rubberduck.Parsing/Binding/Bindings/IndexDefaultBinding.cs

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,21 +116,81 @@ private IBoundExpression Resolve(IBoundExpression lExpression, ArgumentList argu
116116
|| lExpression.Classification == ExpressionClassification.Function
117117
|| lExpression.Classification == ExpressionClassification.Subroutine)
118118
{
119-
return ResolveLExpressionIsPropertyFunctionSubroutine(lExpression, argumentList, expression, defaultMemberResolutionRecursionDepth, containedExpression);
119+
var procedureDeclaration = lExpression.ReferencedDeclaration as IParameterizedDeclaration;
120+
var parameters = procedureDeclaration?.Parameters?.ToList();
121+
if (parameters != null
122+
&& ArgumentListIsCompatible(parameters, argumentList))
123+
{
124+
return ResolveLExpressionIsPropertyFunctionSubroutine(lExpression, argumentList, expression, defaultMemberResolutionRecursionDepth, containedExpression);
125+
}
120126
}
121127

122128
ResolveArgumentList(null, argumentList);
123129
return CreateFailedExpression(lExpression, argumentList, expression, defaultMemberResolutionRecursionDepth > 0);
124130
}
125131

126132
private static IBoundExpression CreateFailedExpression(IBoundExpression lExpression, ArgumentList argumentList, ParserRuleContext context, bool isDefaultMemberResolution)
133+
{
134+
if (IsFailedDefaultMemberResolution(lExpression))
135+
{
136+
return CreateFailedDefaultMemberAccessExpression(lExpression, argumentList, context);
137+
}
138+
139+
return CreateResolutionFailedExpression(lExpression, argumentList, context, isDefaultMemberResolution);
140+
}
141+
142+
private static bool IsFailedDefaultMemberResolution(IBoundExpression lExpression)
143+
{
144+
if (lExpression.Classification == ExpressionClassification.ResolutionFailed)
145+
{
146+
return false;
147+
}
148+
149+
if (IsVariablePropertyFunctionWithoutParameters(lExpression))
150+
{
151+
return true;
152+
}
153+
154+
if (lExpression is IndexExpression indexExpression)
155+
{
156+
var indexedDeclaration = indexExpression.ReferencedDeclaration;
157+
if (indexedDeclaration != null
158+
&& (!indexedDeclaration.IsArray
159+
|| indexExpression.IsArrayAccess))
160+
{
161+
return true;
162+
}
163+
}
164+
165+
if (lExpression is DictionaryAccessExpression dictionaryExpression)
166+
{
167+
var indexedDeclaration = dictionaryExpression.ReferencedDeclaration;
168+
if (indexedDeclaration != null
169+
&& !indexedDeclaration.IsArray)
170+
{
171+
return true;
172+
}
173+
}
174+
175+
return false;
176+
}
177+
178+
private static IBoundExpression CreateResolutionFailedExpression(IBoundExpression lExpression, ArgumentList argumentList, ParserRuleContext context, bool isDefaultMemberResolution)
127179
{
128180
var failedExpr = new ResolutionFailedExpression(context, isDefaultMemberResolution);
129181
failedExpr.AddSuccessfullyResolvedExpression(lExpression);
130182
var argumentExpressions = argumentList.Arguments.Select(arg => arg.Expression);
131183
return failedExpr.JoinAsFailedResolution(context, argumentExpressions);
132184
}
133185

186+
private static IBoundExpression CreateFailedDefaultMemberAccessExpression(IBoundExpression lExpression, ArgumentList argumentList, ParserRuleContext context)
187+
{
188+
var failedExpr = new IndexExpression(lExpression.ReferencedDeclaration, ExpressionClassification.ResolutionFailed, context, lExpression, argumentList, isDefaultMemberAccess: true);
189+
190+
var argumentExpressions = argumentList.Arguments.Select(arg => arg.Expression);
191+
return failedExpr.JoinAsFailedResolution(context, argumentExpressions.Concat(new[] { lExpression }));
192+
}
193+
134194
private IBoundExpression ResolveLExpressionIsVariablePropertyFunctionNoParameters(IBoundExpression lExpression, ArgumentList argumentList, ParserRuleContext expression, int defaultMemberResolutionRecursionDepth, RecursiveDefaultMemberAccessExpression containedExpression)
135195
{
136196
/*
@@ -147,7 +207,7 @@ with a parameter list that cannot accept any parameters and an <argument-list> t
147207
return null;
148208
}
149209

150-
if (indexedDeclaration.IsArray)
210+
if (indexedDeclaration.IsArray && !(lExpression is IndexExpression indexExpression && indexExpression.IsArrayAccess))
151211
{
152212
return ResolveLExpressionDeclaredTypeIsArray(lExpression.ReferencedDeclaration, lExpression.Classification, argumentList, expression, defaultMemberResolutionRecursionDepth, containedExpression);
153213
}
@@ -257,7 +317,7 @@ declared type.
257317
if (ArgumentListIsCompatible(parameters, argumentList))
258318
{
259319
ResolveArgumentList(defaultMember, argumentList);
260-
return new IndexExpression(defaultMember, defaultMemberClassification, expression, _lExpression, argumentList, isDefaultMemberAccess: true, defaultMemberRecursionDepth: defaultMemberResolutionRecursionDepth, containedDefaultMemberRecursionExpression: containedExpression);
320+
return new IndexExpression(defaultMember, ExpressionClassification.Variable, expression, _lExpression, argumentList, isDefaultMemberAccess: true, defaultMemberRecursionDepth: defaultMemberResolutionRecursionDepth, containedDefaultMemberRecursionExpression: containedExpression);
261321
}
262322

263323
/**
@@ -279,7 +339,7 @@ private static bool ArgumentListIsCompatible(ICollection<ParameterDeclaration> p
279339
{
280340
return (parameters.Count >= (argumentList?.Arguments.Count ?? 0)
281341
|| parameters.Any(parameter => parameter.IsParamArray))
282-
&& parameters.Count(parameter => !parameter.IsOptional) <= (argumentList?.Arguments.Count ?? 0);
342+
&& parameters.Count(parameter => !parameter.IsOptional && !parameter.IsParamArray) <= (argumentList?.Arguments.Count ?? 0);
283343
}
284344

285345
private IBoundExpression ResolveRecursiveDefaultMember(Declaration defaultMember, ExpressionClassification defaultMemberClassification, ArgumentList argumentList, ParserRuleContext expression, int defaultMemberResolutionRecursionDepth, RecursiveDefaultMemberAccessExpression containedExpression)
@@ -375,10 +435,10 @@ classification and declared type.
375435
list>. In this case, the index expression references <l-expression> and takes on its classification
376436
and declared type.
377437
378-
Note: We assume compatibility through enforcement by the VBE.
438+
Note: Apart from a check of the number of arguments provided, we assume compatibility through enforcement by the VBE.
379439
*/
380440
ResolveArgumentList(lExpression.ReferencedDeclaration, argumentList);
381-
return new IndexExpression(lExpression.ReferencedDeclaration, lExpression.Classification, expression, _lExpression, argumentList, defaultMemberRecursionDepth: defaultMemberRecursionDepth, containedDefaultMemberRecursionExpression: containedExpression);
441+
return new IndexExpression(lExpression.ReferencedDeclaration, ExpressionClassification.Variable, expression, _lExpression, argumentList, defaultMemberRecursionDepth: defaultMemberRecursionDepth, containedDefaultMemberRecursionExpression: containedExpression);
382442
}
383443

384444
private IBoundExpression ResolveLExpressionIsUnbound(IBoundExpression lExpression, ArgumentList argumentList, ParserRuleContext expression, int defaultMemberResolutionRecursionDepth, RecursiveDefaultMemberAccessExpression containedExpression)

Rubberduck.Parsing/Binding/Bindings/LetCoercionDefaultBinding.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ private static IBoundExpression ResolveViaDefaultMember(IBoundExpression wrapped
117117
return new LetCoercionDefaultMemberAccessExpression(defaultMember, defaultMemberClassification, expression, wrappedExpression, recursionDepth, containedExpression);
118118
}
119119

120-
if (parameters.All(parameter => parameter.IsOptional))
120+
if (parameters.All(parameter => parameter.IsOptional || parameter.IsParamArray))
121121
{
122122
if (!defaultMember.IsObject)
123123
{

Rubberduck.Parsing/Binding/Bindings/ProcedureCoercionDefaultBinding.cs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,32 @@ public sealed class ProcedureCoercionDefaultBinding : IExpressionBinding
1010
{
1111
private readonly ParserRuleContext _expression;
1212
private readonly IExpressionBinding _wrappedExpressionBinding;
13+
private readonly bool _hasExplicitCall;
1314
private IBoundExpression _wrappedExpression;
1415

1516
//This is a wrapper used to model procedure coercion in call statements without arguments.
1617
//The one with arguments is basically an index expression and uses its binding.
1718

1819
public ProcedureCoercionDefaultBinding(
1920
ParserRuleContext expression,
20-
IExpressionBinding wrappedExpressionBinding)
21+
IExpressionBinding wrappedExpressionBinding,
22+
bool hasExplicitCall)
2123
: this(
2224
expression,
23-
(IBoundExpression)null)
25+
(IBoundExpression)null,
26+
hasExplicitCall)
2427
{
2528
_wrappedExpressionBinding = wrappedExpressionBinding;
2629
}
2730

2831
public ProcedureCoercionDefaultBinding(
2932
ParserRuleContext expression,
30-
IBoundExpression wrappedExpression)
33+
IBoundExpression wrappedExpression,
34+
bool hasExplicitCall)
3135
{
3236
_expression = expression;
3337
_wrappedExpression = wrappedExpression;
38+
_hasExplicitCall = hasExplicitCall;
3439
}
3540

3641
public IBoundExpression Resolve()
@@ -40,10 +45,10 @@ public IBoundExpression Resolve()
4045
_wrappedExpression = _wrappedExpressionBinding.Resolve();
4146
}
4247

43-
return Resolve(_wrappedExpression, _expression);
48+
return Resolve(_wrappedExpression, _expression, _hasExplicitCall);
4449
}
4550

46-
private static IBoundExpression Resolve(IBoundExpression wrappedExpression, ParserRuleContext expression)
51+
private static IBoundExpression Resolve(IBoundExpression wrappedExpression, ParserRuleContext expression, bool hasExplicitCall)
4752
{
4853
//Procedure coercion only happens for expressions classified as variables.
4954
if (wrappedExpression.Classification != ExpressionClassification.Variable)
@@ -55,8 +60,8 @@ private static IBoundExpression Resolve(IBoundExpression wrappedExpression, Pars
5560
if (wrappedDeclaration == null
5661
|| !wrappedDeclaration.IsObject
5762
&& !(wrappedDeclaration.IsObjectArray
58-
&& wrappedExpression is IndexExpression indexExpression
59-
&& indexExpression.IsArrayAccess))
63+
&& wrappedExpression is IndexExpression arrayExpression
64+
&& arrayExpression.IsArrayAccess))
6065
{
6166
return wrappedExpression;
6267
}
@@ -66,6 +71,15 @@ private static IBoundExpression Resolve(IBoundExpression wrappedExpression, Pars
6671
var asTypeName = wrappedDeclaration.AsTypeName;
6772
var asTypeDeclaration = wrappedDeclaration.AsTypeDeclaration;
6873

74+
//If there is an explicit call, a non-array (access) index expression or dictionary access expression already count as procedure call.
75+
if (hasExplicitCall
76+
&& (wrappedExpression is IndexExpression indexExpression
77+
&& !indexExpression.IsArrayAccess
78+
|| wrappedExpression is DictionaryAccessExpression))
79+
{
80+
return wrappedExpression;
81+
}
82+
6983
return ResolveViaDefaultMember(wrappedExpression, asTypeName, asTypeDeclaration, expression);
7084
}
7185

@@ -94,7 +108,7 @@ private static IBoundExpression ResolveViaDefaultMember(IBoundExpression wrapped
94108
var defaultMemberClassification = DefaultMemberClassification(defaultMember);
95109

96110
var parameters = ((IParameterizedDeclaration)defaultMember).Parameters.ToList();
97-
if (parameters.All(parameter => parameter.IsOptional))
111+
if (parameters.All(parameter => parameter.IsOptional || parameter.IsParamArray))
98112
{
99113
//We found some default member accepting the empty argument list. So, we are done.
100114
return new ProcedureCoercionExpression(defaultMember, defaultMemberClassification, expression, wrappedExpression);

Rubberduck.Parsing/Binding/DefaultBindingContext.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,11 @@ private IExpressionBinding Visit(Declaration module, Declaration parent, VBAPars
6969
{
7070
return new IndexDefaultBinding(expression.lExpression(), lExpressionBinding, argList);
7171
}
72+
73+
return new ProcedureCoercionDefaultBinding(expression.lExpression(), lExpressionBinding, false);
7274
}
7375

74-
return new ProcedureCoercionDefaultBinding(expression.lExpression(), lExpressionBinding);
76+
return new ProcedureCoercionDefaultBinding(expression.lExpression(), lExpressionBinding, true);
7577
}
7678

7779
private static void SetLeftMatch(IExpressionBinding binding, int argumentCount)

Rubberduck.Parsing/VBA/ReferenceManagement/BoundExpressionVisitor.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using Rubberduck.Parsing.Annotations;
54
using Rubberduck.Parsing.Binding;
65
using Rubberduck.Parsing.Symbols;

0 commit comments

Comments
 (0)