1+ using System ;
2+ using System . Collections . Generic ;
3+ using Rubberduck . Parsing . Symbols ;
4+ using System . Linq ;
5+ using Antlr4 . Runtime ;
6+ using Rubberduck . Parsing . Grammar ;
7+
8+ namespace Rubberduck . Parsing . Binding
9+ {
10+ public sealed class LetCoercionDefaultBinding : IExpressionBinding
11+ {
12+ private readonly ParserRuleContext _expression ;
13+ private readonly IExpressionBinding _wrappedExpressionBinding ;
14+ private IBoundExpression _wrappedExpression ;
15+ private readonly bool _isAssignment ;
16+
17+ private const int DEFAULT_MEMBER_RECURSION_LIMIT = 32 ;
18+
19+ //This is a wrapper used to model Let coercion for object types.
20+
21+ public LetCoercionDefaultBinding (
22+ ParserRuleContext expression ,
23+ IExpressionBinding wrappedExpressionBinding ,
24+ bool isAssignment = false )
25+ : this (
26+ expression ,
27+ ( IBoundExpression ) null ,
28+ isAssignment )
29+ {
30+ _wrappedExpressionBinding = wrappedExpressionBinding ;
31+ }
32+
33+ public LetCoercionDefaultBinding (
34+ ParserRuleContext expression ,
35+ IBoundExpression wrappedExpression ,
36+ bool isAssignment = false )
37+ {
38+ _expression = expression ;
39+ _wrappedExpression = wrappedExpression ;
40+ _isAssignment = isAssignment ;
41+ }
42+
43+ public IBoundExpression Resolve ( )
44+ {
45+ if ( _wrappedExpressionBinding != null )
46+ {
47+ _wrappedExpression = _wrappedExpressionBinding . Resolve ( ) ;
48+ }
49+
50+ return Resolve ( _wrappedExpression , _expression , _isAssignment ) ;
51+ }
52+
53+ private static IBoundExpression Resolve ( IBoundExpression wrappedExpression , ParserRuleContext expression , bool isAssignment )
54+ {
55+ if ( wrappedExpression . Classification == ExpressionClassification . ResolutionFailed )
56+ {
57+ return wrappedExpression ;
58+ }
59+
60+ var wrappedDeclaration = wrappedExpression . ReferencedDeclaration ;
61+
62+ if ( wrappedDeclaration == null
63+ || ! wrappedDeclaration . IsObject
64+ && ! ( wrappedDeclaration . IsObjectArray
65+ && wrappedExpression is IndexExpression indexExpression
66+ && indexExpression . IsArrayAccess ) )
67+ {
68+ return wrappedExpression ;
69+ }
70+
71+ //The wrapped declaration is of a specific class type or Object.
72+
73+ if ( wrappedExpression . Classification == ExpressionClassification . Unbound )
74+ {
75+ //This should actually not be possible since an unbound expression cannot have a referenced declaration.
76+ //Apart from this, we can only deal with the type Object.
77+ return new LetCoercionDefaultMemberAccessExpression ( null , ExpressionClassification . Unbound , expression , wrappedExpression , 1 , null ) ;
78+ }
79+
80+ var asTypeName = wrappedDeclaration . AsTypeName ;
81+ var asTypeDeclaration = wrappedDeclaration . AsTypeDeclaration ;
82+
83+ return ResolveViaDefaultMember ( wrappedExpression , asTypeName , asTypeDeclaration , expression , isAssignment ) ;
84+ }
85+
86+ private static IBoundExpression CreateFailedExpression ( IBoundExpression lExpression )
87+ {
88+ var failedExpr = new ResolutionFailedExpression ( ) ;
89+ failedExpr . AddSuccessfullyResolvedExpression ( lExpression ) ;
90+ return failedExpr ;
91+ }
92+
93+ private static IBoundExpression ResolveViaDefaultMember ( IBoundExpression wrappedExpression , string asTypeName , Declaration asTypeDeclaration , ParserRuleContext expression , bool isAssignment , int recursionDepth = 1 , RecursiveDefaultMemberAccessExpression containedExpression = null )
94+ {
95+ if ( Tokens . Variant . Equals ( asTypeName , StringComparison . InvariantCultureIgnoreCase )
96+ || Tokens . Object . Equals ( asTypeName , StringComparison . InvariantCultureIgnoreCase ) )
97+ {
98+ // We cannot know the the default member in this case, so return an unbound member call.
99+ return new LetCoercionDefaultMemberAccessExpression ( null , ExpressionClassification . Unbound , expression , wrappedExpression , recursionDepth , containedExpression ) ;
100+ }
101+
102+ var defaultMember = ( asTypeDeclaration as ClassModuleDeclaration ) ? . DefaultMember ;
103+ if ( defaultMember == null
104+ || ! IsPropertyGetLetFunctionProcedure ( defaultMember )
105+ || ! IsPublic ( defaultMember ) )
106+ {
107+ return CreateFailedExpression ( wrappedExpression ) ;
108+ }
109+
110+ var defaultMemberClassification = DefaultMemberClassification ( defaultMember ) ;
111+
112+ var parameters = ( ( IParameterizedDeclaration ) defaultMember ) . Parameters . ToList ( ) ;
113+ if ( isAssignment
114+ && defaultMember . DeclarationType == DeclarationType . PropertyLet
115+ && IsCompatibleWithOneNonObjectParameter ( parameters ) )
116+ {
117+ //This is a Let assignment. So, finding a Property Let with one non object paramter means we are done.
118+ return new LetCoercionDefaultMemberAccessExpression ( defaultMember , defaultMemberClassification , expression , wrappedExpression , recursionDepth , containedExpression ) ;
119+ }
120+
121+ if ( parameters . All ( parameter => parameter . IsOptional ) )
122+ {
123+ if ( ! defaultMember . IsObject )
124+ {
125+ //We found a property Get of Function default member returning a value type.
126+ //This might also be applicable in case of an assignment, because only the Get will be assigned as default member if both Get and Let exist.
127+ return new LetCoercionDefaultMemberAccessExpression ( defaultMember , defaultMemberClassification , expression , wrappedExpression , recursionDepth , containedExpression ) ;
128+ }
129+
130+ if ( DEFAULT_MEMBER_RECURSION_LIMIT >= recursionDepth )
131+ {
132+ //The default member returns an object type. So, we have to recurse.
133+ return ResolveRecursiveDefaultMember ( wrappedExpression , defaultMember , defaultMemberClassification , expression , isAssignment , recursionDepth , containedExpression ) ;
134+ }
135+ }
136+
137+ return CreateFailedExpression ( wrappedExpression ) ;
138+ }
139+
140+ private static IBoundExpression ResolveRecursiveDefaultMember ( IBoundExpression wrappedExpression , Declaration defaultMember , ExpressionClassification defaultMemberClassification , ParserRuleContext expression , bool isAssignment , int recursionDepth , RecursiveDefaultMemberAccessExpression containedExpression )
141+ {
142+ var defaultMemberAsTypeName = defaultMember . AsTypeName ;
143+ var defaultMemberAsTypeDeclaration = defaultMember . AsTypeDeclaration ;
144+
145+ var defaultMemberExpression = new RecursiveDefaultMemberAccessExpression ( defaultMember , defaultMemberClassification , expression , recursionDepth , containedExpression ) ;
146+
147+ return ResolveViaDefaultMember (
148+ wrappedExpression ,
149+ defaultMemberAsTypeName ,
150+ defaultMemberAsTypeDeclaration ,
151+ expression ,
152+ isAssignment ,
153+ recursionDepth + 1 ,
154+ defaultMemberExpression ) ;
155+ }
156+
157+ private static bool IsCompatibleWithOneNonObjectParameter ( IReadOnlyCollection < ParameterDeclaration > parameters )
158+ {
159+ return parameters . Count ( parameter => ! parameter . IsObject ) == 1
160+ && parameters . Any ( parameter => ! parameter . IsOptional && ! parameter . IsObject )
161+ || parameters . All ( parameter => parameter . IsOptional )
162+ && parameters . Any ( parameter => ! parameter . IsObject ) ;
163+ }
164+
165+ private static bool IsPropertyGetLetFunctionProcedure ( Declaration declaration )
166+ {
167+ var declarationType = declaration . DeclarationType ;
168+ return declarationType == DeclarationType . PropertyGet
169+ || declarationType == DeclarationType . PropertyLet
170+ || declarationType == DeclarationType . Function
171+ || declarationType == DeclarationType . Procedure ;
172+ }
173+
174+ private static bool IsPublic ( Declaration declaration )
175+ {
176+ var accessibility = declaration . Accessibility ;
177+ return accessibility == Accessibility . Global
178+ || accessibility == Accessibility . Implicit
179+ || accessibility == Accessibility . Public ;
180+ }
181+
182+ private static ExpressionClassification DefaultMemberClassification ( Declaration defaultMember )
183+ {
184+ if ( defaultMember . DeclarationType . HasFlag ( DeclarationType . Property ) )
185+ {
186+ return ExpressionClassification . Property ;
187+ }
188+
189+ if ( defaultMember . DeclarationType == DeclarationType . Procedure )
190+ {
191+ return ExpressionClassification . Subroutine ;
192+ }
193+
194+ return ExpressionClassification . Function ;
195+ }
196+ }
197+ }
0 commit comments