4
4
using Rubberduck . Parsing . VBA ;
5
5
using System . Diagnostics ;
6
6
using System . Linq ;
7
+ using Rubberduck . VBEditor ;
7
8
8
9
namespace Rubberduck . Inspections
9
10
{
@@ -52,13 +53,7 @@ public static bool RequiresSetAssignment(IdentifierReference reference, IDeclara
52
53
{
53
54
// get the members of the returning type, a default member could make us lie otherwise
54
55
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 ) ;
62
57
}
63
58
64
59
// 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
75
70
return false ;
76
71
}
77
72
78
- if ( expression is VBAParser . NewExprContext )
73
+
74
+ var module = Declaration . GetModuleParent ( reference . ParentScoping ) ;
75
+
76
+ if ( expression is VBAParser . NewExprContext newExpr )
79
77
{
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.
81
97
return true ;
82
98
}
83
99
@@ -93,20 +109,48 @@ public static bool RequiresSetAssignment(IdentifierReference reference, IDeclara
93
109
}
94
110
95
111
// todo resolve expression return type
96
- var project = Declaration . GetProjectParent ( reference . ParentScoping ) ;
97
- var module = Declaration . GetModuleParent ( reference . ParentScoping ) ;
98
112
113
+ //Covers the case of a single variable on the RHS of the assignment.
99
114
var simpleName = expression . GetDescendent < VBAParser . SimpleNameExprContext > ( ) ;
100
- if ( simpleName != null )
115
+ if ( simpleName != null && simpleName . GetText ( ) == expression . GetText ( ) )
101
116
{
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 ) ) ;
104
125
}
105
126
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.
106
130
// is the reference referring to something else in scope that's a object?
107
131
return declarationFinderProvider . DeclarationFinder . MatchName ( expression . GetText ( ) )
108
132
. Any ( decl => ( decl . DeclarationType . HasFlag ( DeclarationType . ClassModule ) || Tokens . Object . Equals ( decl . AsTypeName ) )
109
133
&& AccessibilityCheck . IsAccessible ( project , module , reference . ParentScoping , decl ) ) ;
110
134
}
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
+ }
111
155
}
112
156
}
0 commit comments