Skip to content

Commit 7477951

Browse files
author
Andrin Meier
committed
implement "procedure pointer binding context"
1 parent 8a68ad4 commit 7477951

15 files changed

+576
-56
lines changed

Rubberduck.Parsing/Binding/BindingService.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,26 @@ namespace Rubberduck.Parsing.Binding
77
public sealed class BindingService
88
{
99
private readonly IBindingContext _typedBindingContext;
10+
private readonly IBindingContext _procedurePointerBindingContext;
1011

11-
public BindingService(IBindingContext typedBindingContext)
12+
public BindingService(IBindingContext typedBindingContext, IBindingContext procedurePointerBindingContext)
1213
{
1314
_typedBindingContext = typedBindingContext;
15+
_procedurePointerBindingContext = procedurePointerBindingContext;
1416
}
1517

16-
public IBoundExpression Resolve(Declaration module, Declaration parent, string expression)
18+
public IBoundExpression ResolveType(Declaration module, Declaration parent, string expression)
1719
{
1820
var expr = Parse(expression);
1921
return _typedBindingContext.Resolve(module, parent, expr);
2022
}
2123

24+
public IBoundExpression ResolveProcedurePointer(Declaration module, Declaration parent, string expression)
25+
{
26+
var expr = Parse(expression);
27+
return _procedurePointerBindingContext.Resolve(module, parent, expr);
28+
}
29+
2230
private VBAExpressionParser.ExpressionContext Parse(string expression)
2331
{
2432
var stream = new AntlrInputStream(expression);
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using Antlr4.Runtime;
2+
using Rubberduck.Parsing.Symbols;
3+
4+
namespace Rubberduck.Parsing.Binding
5+
{
6+
public sealed class MemberAccessProcedurePointerBinding : IExpressionBinding
7+
{
8+
private readonly DeclarationFinder _declarationFinder;
9+
private readonly Declaration _project;
10+
private readonly Declaration _module;
11+
private readonly Declaration _parent;
12+
private readonly VBAExpressionParser.MemberAccessExpressionContext _memberAccessExpression;
13+
private readonly VBAExpressionParser.MemberAccessExprContext _memberAccessExpr;
14+
private readonly IExpressionBinding _lExpressionBinding;
15+
16+
public MemberAccessProcedurePointerBinding(
17+
DeclarationFinder declarationFinder,
18+
Declaration module,
19+
Declaration parent,
20+
VBAExpressionParser.MemberAccessExpressionContext expression,
21+
IExpressionBinding lExpressionBinding)
22+
{
23+
_declarationFinder = declarationFinder;
24+
_project = module.ParentDeclaration;
25+
_module = module;
26+
_parent = parent;
27+
_memberAccessExpression = expression;
28+
_lExpressionBinding = lExpressionBinding;
29+
}
30+
31+
public MemberAccessProcedurePointerBinding(
32+
DeclarationFinder declarationFinder,
33+
Declaration module,
34+
Declaration parent,
35+
VBAExpressionParser.MemberAccessExprContext expression,
36+
IExpressionBinding lExpressionBinding)
37+
{
38+
_declarationFinder = declarationFinder;
39+
_project = module.ParentDeclaration;
40+
_module = module;
41+
_parent = parent;
42+
_memberAccessExpr = expression;
43+
_lExpressionBinding = lExpressionBinding;
44+
}
45+
46+
private ParserRuleContext GetExpressionContext()
47+
{
48+
if (_memberAccessExpression != null)
49+
{
50+
return _memberAccessExpression;
51+
}
52+
return _memberAccessExpr;
53+
}
54+
55+
private string GetUnrestrictedName()
56+
{
57+
if (_memberAccessExpression != null)
58+
{
59+
return ExpressionName.GetName(_memberAccessExpression.unrestrictedName());
60+
}
61+
return ExpressionName.GetName(_memberAccessExpr.unrestrictedName());
62+
}
63+
64+
public IBoundExpression Resolve()
65+
{
66+
IBoundExpression boundExpression = null;
67+
var lExpression = _lExpressionBinding.Resolve();
68+
if (lExpression == null)
69+
{
70+
return null;
71+
}
72+
string name = GetUnrestrictedName();
73+
if (lExpression.Classification != ExpressionClassification.ProceduralModule)
74+
{
75+
return null;
76+
}
77+
boundExpression = ResolveMemberInModule(lExpression, name, lExpression.ReferencedDeclaration, DeclarationType.Function, ExpressionClassification.Function);
78+
if (boundExpression != null)
79+
{
80+
return boundExpression;
81+
}
82+
boundExpression = ResolveMemberInModule(lExpression, name, lExpression.ReferencedDeclaration, DeclarationType.Procedure, ExpressionClassification.Subroutine);
83+
if (boundExpression != null)
84+
{
85+
return boundExpression;
86+
}
87+
return boundExpression;
88+
}
89+
90+
private IBoundExpression ResolveMemberInModule(IBoundExpression lExpression, string name, Declaration module, DeclarationType memberType, ExpressionClassification classification)
91+
{
92+
/*
93+
A member access expression under the procedure pointer binding context is valid only if <l-
94+
expression> is classified as a procedural module, this procedural module has an accessible function
95+
or subroutine with the same name value as <unrestricted-name>, and <unrestricted-name> either
96+
does not specify a type character or specifies a type character whose associated type matches the
97+
declared type of the function or subroutine. In this case, the member access expression is classified
98+
as a function or subroutine, respectively.
99+
*/
100+
// AddressOf is only allowed in the same project. See The "procedure pointer binding context" for "simple name expressions" section in the MS-VBAL document.
101+
var enclosingProjectType = _declarationFinder.FindMemberEnclosedProjectInModule(_project, _module, _parent, module, name, memberType);
102+
if (enclosingProjectType != null)
103+
{
104+
return new MemberAccessExpression(enclosingProjectType, classification, GetExpressionContext(), lExpression);
105+
}
106+
return null;
107+
}
108+
}
109+
}

Rubberduck.Parsing/Binding/MemberAccessTypeBinding.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ public IBoundExpression Resolve()
6565
{
6666
IBoundExpression boundExpression = null;
6767
var lExpression = _lExpressionBinding.Resolve();
68+
if (lExpression == null)
69+
{
70+
return null;
71+
}
6872
string unrestrictedName = GetUnrestrictedName();
6973
boundExpression = ResolveLExpressionIsProject(lExpression, unrestrictedName);
7074
if (boundExpression != null)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using Antlr4.Runtime;
2+
using Rubberduck.Parsing.Symbols;
3+
4+
namespace Rubberduck.Parsing.Binding
5+
{
6+
public sealed class ProcedurePointerBindingContext : IBindingContext
7+
{
8+
private readonly DeclarationFinder _declarationFinder;
9+
10+
public ProcedurePointerBindingContext(DeclarationFinder declarationFinder)
11+
{
12+
_declarationFinder = declarationFinder;
13+
}
14+
15+
public IBoundExpression Resolve(Declaration module, Declaration parent, ParserRuleContext expression)
16+
{
17+
dynamic dynamicExpression = expression;
18+
IExpressionBinding bindingTree = Visit(module, parent, dynamicExpression);
19+
if (bindingTree != null)
20+
{
21+
return bindingTree.Resolve();
22+
}
23+
return null;
24+
}
25+
26+
private IExpressionBinding Visit(Declaration module, Declaration parent, VBAExpressionParser.LExprContext expression)
27+
{
28+
dynamic lexpr = expression.lExpression();
29+
return Visit(module, parent, lexpr);
30+
}
31+
32+
private IExpressionBinding Visit(Declaration module, Declaration parent, VBAExpressionParser.SimpleNameExprContext expression)
33+
{
34+
var simpleNameExpression = expression.simpleNameExpression();
35+
return Visit(module, parent, simpleNameExpression);
36+
}
37+
38+
private IExpressionBinding Visit(Declaration module, Declaration parent, VBAExpressionParser.SimpleNameExpressionContext expression)
39+
{
40+
return new SimpleNameProcedurePointerBinding(_declarationFinder, module, parent, expression);
41+
}
42+
43+
private IExpressionBinding Visit(Declaration module, Declaration parent, VBAExpressionParser.MemberAccessExprContext expression)
44+
{
45+
dynamic lExpression = expression.lExpression();
46+
var lExpressionBinding = Visit(module, parent, lExpression);
47+
return new MemberAccessProcedurePointerBinding(_declarationFinder, module, parent, expression, lExpressionBinding);
48+
}
49+
50+
private IExpressionBinding Visit(Declaration module, Declaration parent, VBAExpressionParser.MemberAccessExpressionContext expression)
51+
{
52+
dynamic lExpression = expression.lExpression();
53+
var lExpressionBinding = Visit(module, parent, lExpression);
54+
return new MemberAccessProcedurePointerBinding(_declarationFinder, module, parent, expression, lExpressionBinding);
55+
}
56+
}
57+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using Rubberduck.Parsing.Symbols;
2+
3+
namespace Rubberduck.Parsing.Binding
4+
{
5+
public sealed class SimpleNameProcedurePointerBinding : IExpressionBinding
6+
{
7+
private readonly DeclarationFinder _declarationFinder;
8+
private readonly Declaration _project;
9+
private readonly Declaration _module;
10+
private readonly Declaration _parent;
11+
private readonly VBAExpressionParser.SimpleNameExpressionContext _expression;
12+
13+
public SimpleNameProcedurePointerBinding(
14+
DeclarationFinder declarationFinder,
15+
Declaration module,
16+
Declaration parent,
17+
VBAExpressionParser.SimpleNameExpressionContext expression)
18+
{
19+
_declarationFinder = declarationFinder;
20+
_project = module.ParentDeclaration;
21+
_module = module;
22+
_parent = parent;
23+
_expression = expression;
24+
}
25+
26+
public IBoundExpression Resolve()
27+
{
28+
IBoundExpression boundExpression = null;
29+
string name = ExpressionName.GetName(_expression.name());
30+
boundExpression = ResolveEnclosingModule(name);
31+
if (boundExpression != null)
32+
{
33+
return boundExpression;
34+
}
35+
boundExpression = ResolveEnclosingProject(name);
36+
if (boundExpression != null)
37+
{
38+
return boundExpression;
39+
}
40+
boundExpression = ResolveOtherModuleInEnclosingProject(name);
41+
return boundExpression;
42+
}
43+
44+
private IBoundExpression ResolveEnclosingModule(string name)
45+
{
46+
/*
47+
Enclosing Module namespace: A function, subroutine or property with a Property Get defined
48+
at the module-level in the enclosing module.
49+
*/
50+
var function = _declarationFinder.FindMemberEnclosingModule(_project, _module, _parent, name, DeclarationType.Function);
51+
if (function != null)
52+
{
53+
return new SimpleNameExpression(function, ExpressionClassification.Function, _expression);
54+
}
55+
var subroutine = _declarationFinder.FindMemberEnclosingModule(_project, _module, _parent, name, DeclarationType.Procedure);
56+
if (subroutine != null)
57+
{
58+
return new SimpleNameExpression(subroutine, ExpressionClassification.Subroutine, _expression);
59+
}
60+
var propertyGet = _declarationFinder.FindMemberEnclosingModule(_project, _module, _parent, name, DeclarationType.PropertyGet);
61+
if (propertyGet != null)
62+
{
63+
return new SimpleNameExpression(propertyGet, ExpressionClassification.Property, _expression);
64+
}
65+
return null;
66+
}
67+
68+
private IBoundExpression ResolveEnclosingProject(string name)
69+
{
70+
/*
71+
Enclosing Project namespace: The enclosing project itself or a procedural module contained in
72+
the enclosing project.
73+
*/
74+
if (_project.Project.Name == name)
75+
{
76+
return new SimpleNameExpression(_project, ExpressionClassification.Project, _expression);
77+
}
78+
if (_module.DeclarationType == DeclarationType.ProceduralModule && _module.IdentifierName == name)
79+
{
80+
return new SimpleNameExpression(_module, ExpressionClassification.ProceduralModule, _expression);
81+
}
82+
var proceduralModuleEnclosingProject = _declarationFinder.FindModuleEnclosingProjectWithoutEnclosingModule(_project, _module, name, DeclarationType.ProceduralModule);
83+
if (proceduralModuleEnclosingProject != null)
84+
{
85+
return new SimpleNameExpression(proceduralModuleEnclosingProject, ExpressionClassification.ProceduralModule, _expression);
86+
}
87+
return null;
88+
}
89+
90+
private IBoundExpression ResolveOtherModuleInEnclosingProject(string name)
91+
{
92+
/*
93+
Other Procedural Module in Enclosing Project namespace: An accessible function,
94+
subroutine or property with a Property Get defined in a procedural module within the enclosing
95+
project other than the enclosing module.
96+
*/
97+
var function = _declarationFinder.FindMemberEnclosedProjectWithoutEnclosingModule(_project, _module, _parent, name, DeclarationType.Function, DeclarationType.ProceduralModule);
98+
if (function != null)
99+
{
100+
return new SimpleNameExpression(function, ExpressionClassification.Function, _expression);
101+
}
102+
var subroutine = _declarationFinder.FindMemberEnclosedProjectWithoutEnclosingModule(_project, _module, _parent, name, DeclarationType.Procedure, DeclarationType.ProceduralModule);
103+
if (subroutine != null)
104+
{
105+
return new SimpleNameExpression(subroutine, ExpressionClassification.Subroutine, _expression);
106+
}
107+
var propertyGet = _declarationFinder.FindMemberEnclosedProjectWithoutEnclosingModule(_project, _module, _parent, name, DeclarationType.PropertyGet, DeclarationType.ProceduralModule);
108+
if (propertyGet != null)
109+
{
110+
return new SimpleNameExpression(propertyGet, ExpressionClassification.Property, _expression);
111+
}
112+
return null;
113+
}
114+
}
115+
}

Rubberduck.Parsing/Rubberduck.Parsing.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@
7777
<Compile Include="Binding\BindingService.cs" />
7878
<Compile Include="Binding\BoundExpression.cs" />
7979
<Compile Include="Binding\MemberAccessExpression.cs" />
80+
<Compile Include="Binding\MemberAccessProcedurePointerBinding.cs" />
8081
<Compile Include="Binding\MemberAccessTypeBinding.cs" />
82+
<Compile Include="Binding\ProcedurePointerBindingContext.cs" />
83+
<Compile Include="Binding\SimpleNameProcedurePointerBinding.cs" />
8184
<Compile Include="Binding\TypeBindingContext.cs" />
8285
<Compile Include="Binding\ExpressionClassification.cs" />
8386
<Compile Include="Binding\ExpressionName.cs" />
@@ -194,6 +197,7 @@
194197
<Compile Include="Preprocessing\VBAPreprocessorVisitor.cs" />
195198
<Compile Include="StaTaskScheduler.cs" />
196199
<Compile Include="Symbols\AccessibilityCheck.cs" />
200+
<Compile Include="Symbols\BindingMigrationHelper.cs" />
197201
<Compile Include="Symbols\BoundExpressionVisitor.cs" />
198202
<Compile Include="Symbols\ClassModuleDeclaration.cs" />
199203
<Compile Include="Symbols\CommentExtensions.cs" />

Rubberduck.Parsing/Symbols/AccessibilityCheck.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ public static class AccessibilityCheck
44
{
55
public static bool IsAccessible(Declaration callingProject, Declaration callingModule, Declaration callingParent, Declaration callee)
66
{
7-
if (callee.DeclarationType == DeclarationType.Project)
7+
if (callee.DeclarationType.HasFlag(DeclarationType.Project))
88
{
99
return true;
1010
}
11-
if (callee.DeclarationType == DeclarationType.ClassModule || callee.DeclarationType == DeclarationType.ProceduralModule)
11+
if (callee.DeclarationType.HasFlag(DeclarationType.Module))
1212
{
1313
return IsModuleAccessible(callingProject, callingModule, callee);
1414
}
@@ -28,7 +28,7 @@ public static bool IsModuleAccessible(Declaration callingProject, Declaration ca
2828
{
2929
return validAccessibility;
3030
}
31-
if (calleeModule.DeclarationType == DeclarationType.ProceduralModule)
31+
if (calleeModule.DeclarationType.HasFlag(DeclarationType.ProceduralModule))
3232
{
3333
bool isPrivate = ((ProceduralModuleDeclaration)calleeModule).IsPrivateModule;
3434
return validAccessibility && !isPrivate;
@@ -56,17 +56,17 @@ public static bool IsMemberAccessible(Declaration callingProject, Declaration ca
5656
return true;
5757
}
5858
var callerIsSubroutineOrProperty = callingParent.DeclarationType.HasFlag(DeclarationType.Property)
59-
|| callingParent.DeclarationType == DeclarationType.Function
60-
|| callingParent.DeclarationType == DeclarationType.Procedure;
59+
|| callingParent.DeclarationType.HasFlag(DeclarationType.Function)
60+
|| callingParent.DeclarationType.HasFlag(DeclarationType.Procedure);
6161
var calleeHasSameParent = callingParent.Equals(callingParent.ParentScopeDeclaration);
62-
if (callerIsSubroutineOrProperty)
62+
if (callerIsSubroutineOrProperty && calleeHasSameParent)
6363
{
6464
return calleeHasSameParent;
6565
}
6666
var memberModule = Declaration.GetMemberModule(calleeMember);
67-
if (IsModuleAccessible(callingProject, callingModule, memberModule))
67+
if (IsModuleAccessible(callingProject, callingModule, memberModule) && calleeMember.ParentScopeDeclaration.DeclarationType.HasFlag(DeclarationType.Module))
6868
{
69-
if (calleeMember.DeclarationType == DeclarationType.EnumerationMember || calleeMember.DeclarationType == DeclarationType.UserDefinedTypeMember)
69+
if (calleeMember.DeclarationType.HasFlag(DeclarationType.EnumerationMember) || calleeMember.DeclarationType.HasFlag(DeclarationType.UserDefinedTypeMember))
7070
{
7171
return IsValidAccessibility(calleeMember.ParentDeclaration);
7272
}

0 commit comments

Comments
 (0)