44using Rubberduck . Parsing . VBA ;
55using System . Diagnostics ;
66using System . Linq ;
7+ using Rubberduck . VBEditor ;
78
89namespace Rubberduck . Inspections
910{
@@ -52,13 +53,7 @@ public static bool RequiresSetAssignment(IdentifierReference reference, IDeclara
5253 {
5354 // get the members of the returning type, a default member could make us lie otherwise
5455 var classModule = declaration . AsTypeDeclaration as ClassModuleDeclaration ;
55- if ( classModule ? . DefaultMember == null )
56- {
57- return true ;
58- }
59- var parameters = ( classModule . DefaultMember as IParameterizedDeclaration ) ? . Parameters ;
60- // assign declaration is an object without a default parameterless (or with all parameters optional) member - LHS needs a 'Set' keyword.
61- return parameters != null && parameters . All ( p => p . IsOptional ) ;
56+ return ! HasPotentiallyNonObjectParameterlessDefaultMember ( classModule ) ;
6257 }
6358
6459 // assigned declaration is a variant. we need to know about the RHS of the assignment.
@@ -75,9 +70,30 @@ public static bool RequiresSetAssignment(IdentifierReference reference, IDeclara
7570 return false ;
7671 }
7772
78- if ( expression is VBAParser . NewExprContext )
73+
74+ var module = Declaration . GetModuleParent ( reference . ParentScoping ) ;
75+
76+ if ( expression is VBAParser . NewExprContext newExpr )
7977 {
80- // RHS expression is newing up an object reference - LHS needs a 'Set' keyword:
78+ var newTypeExpression = newExpr . expression ( ) ;
79+
80+ // todo resolve expression type
81+
82+ //Covers the case of a single type on the RHS of the assignment.
83+ var simpleTypeName = newTypeExpression . GetDescendent < VBAParser . SimpleNameExprContext > ( ) ;
84+ if ( simpleTypeName != null && simpleTypeName . GetText ( ) == newTypeExpression . GetText ( ) )
85+ {
86+ var qualifiedIdentifierSelection = new QualifiedSelection ( module . QualifiedModuleName ,
87+ simpleTypeName . identifier ( ) . GetSelection ( ) ) ;
88+ var identifierText = simpleTypeName . identifier ( ) . GetText ( ) ;
89+ return declarationFinderProvider . DeclarationFinder . IdentifierReferences ( qualifiedIdentifierSelection )
90+ . Select ( identifierReference => identifierReference . Declaration )
91+ . Where ( decl => identifierText == decl . IdentifierName )
92+ . OfType < ClassModuleDeclaration > ( )
93+ . Any ( typeDecl => ! HasPotentiallyNonObjectParameterlessDefaultMember ( typeDecl ) ) ;
94+ }
95+ //Here, we err on the side of false-positives, but that seems more appropriate than not to treat qualified type expressions incorrectly.
96+ //Whether there is a legitimate use here for default members is questionable anyway.
8197 return true ;
8298 }
8399
@@ -93,20 +109,48 @@ public static bool RequiresSetAssignment(IdentifierReference reference, IDeclara
93109 }
94110
95111 // todo resolve expression return type
96- var project = Declaration . GetProjectParent ( reference . ParentScoping ) ;
97- var module = Declaration . GetModuleParent ( reference . ParentScoping ) ;
98112
113+ //Covers the case of a single variable on the RHS of the assignment.
99114 var simpleName = expression . GetDescendent < VBAParser . SimpleNameExprContext > ( ) ;
100- if ( simpleName != null )
115+ if ( simpleName != null && simpleName . GetText ( ) == expression . GetText ( ) )
101116 {
102- return declarationFinderProvider . DeclarationFinder . MatchName ( simpleName . identifier ( ) . GetText ( ) )
103- . Any ( d => AccessibilityCheck . IsAccessible ( project , module , reference . ParentScoping , d ) && d . IsObject ) ;
117+ var qualifiedIdentifierSelection = new QualifiedSelection ( module . QualifiedModuleName ,
118+ simpleName . identifier ( ) . GetSelection ( ) ) ;
119+ return declarationFinderProvider . DeclarationFinder . IdentifierReferences ( qualifiedIdentifierSelection )
120+ . Select ( identifierReference => identifierReference . Declaration )
121+ . Where ( decl => decl . IsObject
122+ && simpleName . identifier ( ) . GetText ( ) == decl . IdentifierName )
123+ . Select ( typeDeclaration => typeDeclaration . AsTypeDeclaration as ClassModuleDeclaration )
124+ . Any ( typeDecl => ! HasPotentiallyNonObjectParameterlessDefaultMember ( typeDecl ) ) ;
104125 }
105126
127+ var project = Declaration . GetProjectParent ( reference . ParentScoping ) ;
128+
129+ //todo: Use code path analysis to ensure that we are really picking up the last assignment to the RHS.
106130 // is the reference referring to something else in scope that's a object?
107131 return declarationFinderProvider . DeclarationFinder . MatchName ( expression . GetText ( ) )
108132 . Any ( decl => ( decl . DeclarationType . HasFlag ( DeclarationType . ClassModule ) || Tokens . Object . Equals ( decl . AsTypeName ) )
109133 && AccessibilityCheck . IsAccessible ( project , module , reference . ParentScoping , decl ) ) ;
110134 }
135+
136+ private static bool HasPotentiallyNonObjectParameterlessDefaultMember ( ClassModuleDeclaration classModule )
137+ {
138+ var defaultMember = classModule ? . DefaultMember ;
139+
140+ if ( defaultMember == null )
141+ {
142+ return false ;
143+ }
144+
145+ var parameters = ( defaultMember as IParameterizedDeclaration ) ? . Parameters ;
146+ // assign declaration is an object without a default parameterless (or with all parameters optional) member - LHS needs a 'Set' keyword.
147+ if ( parameters != null && parameters . Any ( p => ! p . IsOptional ) )
148+ {
149+ return false ;
150+ }
151+
152+ var defaultMemberType = defaultMember . AsTypeDeclaration as ClassModuleDeclaration ;
153+ return defaultMemberType == null || HasPotentiallyNonObjectParameterlessDefaultMember ( defaultMemberType ) ;
154+ }
111155 }
112156}
0 commit comments