4
4
using Antlr4 . Runtime ;
5
5
using Rubberduck . Inspections . Abstract ;
6
6
using Rubberduck . Inspections . Results ;
7
+ using Rubberduck . JunkDrawer . Extensions ;
7
8
using Rubberduck . Parsing ;
8
9
using Rubberduck . Parsing . Grammar ;
9
10
using Rubberduck . Parsing . Inspections . Abstract ;
10
11
using Rubberduck . Resources . Inspections ;
11
12
using Rubberduck . Parsing . Symbols ;
12
13
using Rubberduck . Parsing . VBA ;
14
+ using Rubberduck . Parsing . VBA . DeclarationCaching ;
15
+ using Rubberduck . VBEditor ;
13
16
14
17
namespace Rubberduck . Inspections . Concrete
15
18
{
@@ -41,7 +44,7 @@ namespace Rubberduck.Inspections.Concrete
41
44
/// ]]>
42
45
/// </example>
43
46
[ SuppressMessage ( "ReSharper" , "LoopCanBeConvertedToQuery" ) ]
44
- public sealed class UnassignedVariableUsageInspection : InspectionBase
47
+ public sealed class UnassignedVariableUsageInspection : IdentifierReferenceInspectionFromDeclarationsBase
45
48
{
46
49
public UnassignedVariableUsageInspection ( RubberduckParserState state )
47
50
: base ( state ) { }
@@ -55,32 +58,97 @@ public UnassignedVariableUsageInspection(RubberduckParserState state)
55
58
"VBA6.DLL;VBA.Strings.LenB"
56
59
} ;
57
60
58
- protected override IEnumerable < IInspectionResult > DoGetInspectionResults ( )
61
+ protected override IEnumerable < Declaration > ObjectionableDeclarations ( DeclarationFinder finder )
59
62
{
60
- var declarations = State . DeclarationFinder . UserDeclarations ( DeclarationType . Variable )
61
- . Where ( declaration => ! declaration . IsArray &&
62
- State . DeclarationFinder . MatchName ( declaration . AsTypeName )
63
- . All ( d => d . DeclarationType != DeclarationType . UserDefinedType )
64
- && ! declaration . IsSelfAssigned
65
- && ! declaration . References . Any ( reference => reference . IsAssignment ) ) ;
66
-
67
- var excludedDeclarations = BuiltInDeclarations . Where ( decl => IgnoredFunctions . Contains ( decl . QualifiedName . ToString ( ) ) ) . ToList ( ) ;
68
-
69
- return declarations
70
- . Where ( d => d . References . Any ( ) && ! excludedDeclarations . Any ( excl => DeclarationReferencesContainsReference ( excl , d ) ) )
71
- . SelectMany ( d => d . References . Where ( r => ! IsAssignedByRefArgument ( r . ParentScoping , r ) ) )
72
- . Distinct ( )
73
- . Where ( r => ! r . Context . TryGetAncestor < VBAParser . RedimStmtContext > ( out _ ) && ! IsArraySubscriptAssignment ( r ) )
74
- . Select ( r => new IdentifierReferenceInspectionResult ( this ,
75
- string . Format ( InspectionResults . UnassignedVariableUsageInspection , r . IdentifierName ) ,
76
- State ,
77
- r ) ) . ToList ( ) ;
63
+ return finder . UserDeclarations ( DeclarationType . Variable )
64
+ . Where ( declaration => ! declaration . IsArray
65
+ && ! declaration . IsSelfAssigned
66
+ && finder . MatchName ( declaration . AsTypeName )
67
+ . All ( d => d . DeclarationType != DeclarationType . UserDefinedType )
68
+ && ! declaration . References
69
+ . Any ( reference => reference . IsAssignment ) ) ;
78
70
}
79
71
80
- private bool IsAssignedByRefArgument ( Declaration enclosingProcedure , IdentifierReference reference )
72
+ //We override this in order to look up the argument usage exclusion references only once.
73
+ protected override IEnumerable < IdentifierReference > ObjectionableReferences ( DeclarationFinder finder )
74
+ {
75
+ var excludedReferenceSelections = DeclarationsWithExcludedArgumentUsage ( finder )
76
+ . SelectMany ( SingleVariableArgumentSelections )
77
+ . ToHashSet ( ) ;
78
+
79
+ return base . ObjectionableReferences ( finder )
80
+ . Where ( reference => ! excludedReferenceSelections . Contains ( reference . QualifiedSelection ) ) ;
81
+ }
82
+
83
+ private IEnumerable < ModuleBodyElementDeclaration > DeclarationsWithExcludedArgumentUsage ( DeclarationFinder finder )
84
+ {
85
+ var vbaProjects = finder . Projects
86
+ . Where ( project => project . IdentifierName == "VBA" && ! project . IsUserDefined )
87
+ . ToList ( ) ;
88
+
89
+ if ( ! vbaProjects . Any ( ) )
90
+ {
91
+ return new List < ModuleBodyElementDeclaration > ( ) ;
92
+ }
93
+
94
+ var stringModules = vbaProjects
95
+ . Select ( project => finder . FindStdModule ( "Strings" , project , true ) )
96
+ . OfType < ModuleDeclaration > ( )
97
+ . ToList ( ) ;
98
+
99
+ if ( ! stringModules . Any ( ) )
100
+ {
101
+ return new List < ModuleBodyElementDeclaration > ( ) ;
102
+ }
103
+
104
+ return stringModules
105
+ . SelectMany ( module => module . Members )
106
+ . Where ( decl => IgnoredFunctions . Contains ( decl . QualifiedName . ToString ( ) ) )
107
+ . OfType < ModuleBodyElementDeclaration > ( ) ;
108
+ }
109
+
110
+ private static IEnumerable < QualifiedSelection > SingleVariableArgumentSelections ( ModuleBodyElementDeclaration member )
111
+ {
112
+ return member . Parameters
113
+ . SelectMany ( parameter => parameter . ArgumentReferences )
114
+ . Select ( SingleVariableArgumentSelection )
115
+ . Where ( maybeSelection => maybeSelection . HasValue )
116
+ . Select ( selection => selection . Value ) ;
117
+ }
118
+
119
+ private static QualifiedSelection ? SingleVariableArgumentSelection ( ArgumentReference argumentReference )
120
+ {
121
+ var argumentContext = argumentReference . Context as VBAParser . LExprContext ;
122
+ if ( ! ( argumentContext ? . lExpression ( ) is VBAParser . SimpleNameExprContext name ) )
123
+ {
124
+ return null ;
125
+ }
126
+
127
+ return new QualifiedSelection ( argumentReference . QualifiedModuleName , name . GetSelection ( ) ) ;
128
+ }
129
+
130
+ protected override bool IsResultReference ( IdentifierReference reference , DeclarationFinder finder )
131
+ {
132
+ //FIXME: stop filtering out too many results.
133
+ return reference != null
134
+ && ! IsAssignedByRefArgument ( reference . ParentScoping , reference , finder )
135
+ && ! IsArraySubscriptAssignment ( reference )
136
+ && ! reference . Context . TryGetAncestor < VBAParser . RedimStmtContext > ( out _ ) ;
137
+
138
+ }
139
+
140
+ protected override string ResultDescription ( IdentifierReference reference , dynamic properties = null )
141
+ {
142
+ var identifierName = reference . IdentifierName ;
143
+ return string . Format (
144
+ InspectionResults . UnassignedVariableUsageInspection ,
145
+ identifierName ) ;
146
+ }
147
+
148
+ private static bool IsAssignedByRefArgument ( Declaration enclosingProcedure , IdentifierReference reference , DeclarationFinder finder )
81
149
{
82
150
var argExpression = reference . Context . GetAncestor < VBAParser . ArgumentExpressionContext > ( ) ;
83
- var parameter = State . DeclarationFinder . FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly ( argExpression , enclosingProcedure ) ;
151
+ var parameter = finder . FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly ( argExpression , enclosingProcedure ) ;
84
152
85
153
// note: not recursive, by design.
86
154
return parameter != null
@@ -90,28 +158,12 @@ private bool IsAssignedByRefArgument(Declaration enclosingProcedure, IdentifierR
90
158
91
159
private static bool IsArraySubscriptAssignment ( IdentifierReference reference )
92
160
{
161
+ //FIXME: stop returning true for too many cases.
93
162
var isLetAssignment = reference . Context . TryGetAncestor < VBAParser . LetStmtContext > ( out var letStmt ) ;
94
163
var isSetAssignment = reference . Context . TryGetAncestor < VBAParser . SetStmtContext > ( out var setStmt ) ;
95
164
96
- return isLetAssignment && letStmt . lExpression ( ) is VBAParser . IndexExprContext ||
97
- isSetAssignment && setStmt . lExpression ( ) is VBAParser . IndexExprContext ;
98
- }
99
-
100
- private static bool DeclarationReferencesContainsReference ( Declaration parentDeclaration , Declaration target )
101
- {
102
- foreach ( var targetReference in target . References )
103
- {
104
- foreach ( var reference in parentDeclaration . References )
105
- {
106
- var context = ( ParserRuleContext ) reference . Context . Parent ;
107
- if ( context . GetSelection ( ) . Contains ( targetReference . Selection ) )
108
- {
109
- return true ;
110
- }
111
- }
112
- }
113
-
114
- return false ;
165
+ return isLetAssignment && letStmt . lExpression ( ) is VBAParser . IndexExprContext
166
+ || isSetAssignment && setStmt . lExpression ( ) is VBAParser . IndexExprContext ;
115
167
}
116
168
}
117
169
}
0 commit comments