9
9
using Rubberduck . UI . Refactorings ;
10
10
using Rubberduck . Common ;
11
11
using Antlr4 . Runtime ;
12
+ using System . Collections . Generic ;
13
+ using Antlr4 . Runtime . Tree ;
12
14
13
15
namespace Rubberduck . Inspections . QuickFixes
14
16
{
15
17
public class AssignedByValParameterMakeLocalCopyQuickFix : QuickFixBase
16
18
{
17
19
private readonly Declaration _target ;
18
20
private string _localCopyVariableName ;
21
+ private string [ ] _variableNamesAccessibleToProcedureContext ;
19
22
private bool _isQuickFixUnitTest ;
20
- private QuickFixHelper _quickFixHelper ;
21
23
22
24
public AssignedByValParameterMakeLocalCopyQuickFix ( Declaration target , QualifiedSelection selection )
23
25
: base ( target . Context , selection , InspectionsUI . AssignedByValParameterQuickFix )
24
26
{
25
27
_target = target ;
26
28
_isQuickFixUnitTest = false ;
27
29
_localCopyVariableName = "x" + _target . IdentifierName . CapitalizeFirstLetter ( ) ;
28
- _quickFixHelper = new QuickFixHelper ( _target , selection ) ;
30
+ _variableNamesAccessibleToProcedureContext = GetVariableNamesAccessibleToProcedureContext ( _target . Context . Parent . Parent ) ;
29
31
}
30
32
31
33
public override bool CanFixInModule { get { return false ; } }
@@ -46,24 +48,24 @@ public void TESTONLY_FixUsingAutoGeneratedName(string suggestedName = "")
46
48
47
49
public override void Fix ( )
48
50
{
49
-
50
51
SetLocalCopyVariableName ( ) ;
51
52
52
- if ( ! ProposedLocalVariableNameIsValid ( )
53
- || IsCancelled )
53
+ if ( ! ProposedLocalVariableNameIsValid ( ) || IsCancelled )
54
54
{
55
55
return ;
56
56
}
57
57
58
- ModifyBlockToUseLocalCopyVariable ( ) ;
58
+ ReplaceAssignedByValParameterReferences ( ) ;
59
+
60
+ InsertLocalVariableDeclarationAndAssignment ( ) ;
59
61
}
60
62
61
63
private void SetLocalCopyVariableName ( )
62
64
{
63
65
using ( var view = new AssignedByValParameterQuickFixDialog ( _target , Selection ) )
64
66
{
65
67
view . NewName = _localCopyVariableName ;
66
- view . IdentifierNamesAlreadyDeclared = _quickFixHelper . GetIdentifierNamesAccessibleToProcedureContext ( ) ;
68
+ view . IdentifierNamesAlreadyDeclared = _variableNamesAccessibleToProcedureContext ;
67
69
if ( ! _isQuickFixUnitTest )
68
70
{
69
71
view . ShowDialog ( ) ;
@@ -80,43 +82,101 @@ private bool ProposedLocalVariableNameIsValid()
80
82
{
81
83
var validator = new VariableNameValidator ( _localCopyVariableName ) ;
82
84
return validator . IsValidName ( )
83
- && ! _quickFixHelper . GetIdentifierNamesAccessibleToProcedureContext ( ) . Any ( name => name . ToUpper ( ) . Equals ( _localCopyVariableName . ToUpper ( ) ) ) ;
84
- }
85
-
86
- private void ModifyBlockToUseLocalCopyVariable ( )
87
- {
88
- ReplaceAssignedByValParameterReferences ( ) ;
89
-
90
- InsertLocalVariableDeclarationAndAssignment ( GetFirstBlockStartLine ( ) ) ;
85
+ && ! _variableNamesAccessibleToProcedureContext
86
+ . Any ( name => name . ToUpper ( ) . Equals ( _localCopyVariableName . ToUpper ( ) ) ) ;
91
87
}
92
88
93
89
private void ReplaceAssignedByValParameterReferences ( )
94
90
{
91
+ var module = Selection . QualifiedName . Component . CodeModule ;
95
92
foreach ( IdentifierReference identifierReference in _target . References )
96
93
{
97
- _quickFixHelper . ReplaceIdentifierReferenceNameInModule ( identifierReference , _localCopyVariableName ) ;
94
+ module . ReplaceIdentifierReferenceName ( identifierReference , _localCopyVariableName ) ;
98
95
}
99
96
}
100
- private int GetFirstBlockStartLine ( )
101
- {
102
- var blocks = _quickFixHelper . GetBlockStmtContextsForContext ( _target . Context . Parent . Parent ) ;
103
- return blocks . FirstOrDefault ( ) . Start . Line ;
104
- }
105
- private void InsertLocalVariableDeclarationAndAssignment ( int firstBlockLineNumber )
97
+ private void InsertLocalVariableDeclarationAndAssignment ( )
106
98
{
107
- string [ ] newLines = { BuildLocalCopyDeclaration ( ) , BuildLocalCopyAssignment ( ) } ;
108
- _quickFixHelper . InsertAfterCodeModuleLine ( firstBlockLineNumber - 1 , newLines ) ;
99
+ var blocks = QuickFixHelper . GetBlockStmtContextsForContext ( _target . Context . Parent . Parent ) ;
100
+ var firstBlockLineNumber = blocks . FirstOrDefault ( ) . Start . Line ;
101
+
102
+ var module = Selection . QualifiedName . Component . CodeModule ;
103
+ module . InsertLines ( firstBlockLineNumber ++ , BuildLocalCopyDeclaration ( ) ) ;
104
+ module . InsertLines ( firstBlockLineNumber , BuildLocalCopyAssignment ( ) ) ;
109
105
}
110
106
private string BuildLocalCopyDeclaration ( )
111
107
{
112
108
return Tokens . Dim + " " + _localCopyVariableName + " " + Tokens . As
113
109
+ " " + _target . AsTypeName ;
114
110
}
115
-
116
111
private string BuildLocalCopyAssignment ( )
117
112
{
118
113
return ( SymbolList . ValueTypes . Contains ( _target . AsTypeName ) ? string . Empty : Tokens . Set + " " )
119
114
+ _localCopyVariableName + " = " + _target . IdentifierName ;
120
115
}
116
+ private string [ ] GetVariableNamesAccessibleToProcedureContext ( RuleContext ruleContext )
117
+ {
118
+ var allIdentifiers = new HashSet < string > ( ) ;
119
+
120
+ var blocks = QuickFixHelper . GetBlockStmtContextsForContext ( ruleContext ) ;
121
+
122
+ var blockStmtIdentifiers = GetIdentifierNames ( blocks ) ;
123
+
124
+ allIdentifiers . UnionWith ( blockStmtIdentifiers ) ;
125
+
126
+ var args = QuickFixHelper . GetArgContextsForContext ( ruleContext ) ;
127
+
128
+ var potentiallyUnreferencedParameters = GetIdentifierNames ( args ) ;
129
+ allIdentifiers . UnionWith ( potentiallyUnreferencedParameters ) ;
130
+
131
+ //TODO: add module and global scope variableNames.
132
+
133
+ return allIdentifiers . ToArray ( ) ;
134
+ }
135
+ private HashSet < string > GetIdentifierNames ( IReadOnlyList < RuleContext > ruleContexts )
136
+ {
137
+ var identifiers = new HashSet < string > ( ) ;
138
+ foreach ( RuleContext ruleContext in ruleContexts )
139
+ {
140
+ var identifiersForThisContext = GetIdentifierNames ( ruleContext ) ;
141
+ identifiers . UnionWith ( identifiersForThisContext ) ;
142
+ }
143
+ return identifiers ;
144
+ }
145
+ private HashSet < string > GetIdentifierNames ( RuleContext ruleContext )
146
+ {
147
+ //Recursively work through the tree to get all IdentifierContexts
148
+ var results = new HashSet < string > ( ) ;
149
+ var tokenValues = typeof ( Tokens ) . GetFields ( ) . Select ( item => item . GetValue ( null ) ) . Cast < string > ( ) . Select ( item => item ) ;
150
+ var children = GetChildren ( ruleContext ) ;
151
+
152
+ foreach ( IParseTree child in children )
153
+ {
154
+ if ( child is VBAParser . IdentifierContext )
155
+ {
156
+ var childName = Identifier . GetName ( ( VBAParser . IdentifierContext ) child ) ;
157
+ if ( ! tokenValues . Contains ( childName ) )
158
+ {
159
+ results . Add ( childName ) ;
160
+ }
161
+ }
162
+ else
163
+ {
164
+ if ( ! ( child is TerminalNodeImpl ) )
165
+ {
166
+ results . UnionWith ( GetIdentifierNames ( ( RuleContext ) child ) ) ;
167
+ }
168
+ }
169
+ }
170
+ return results ;
171
+ }
172
+ private static List < IParseTree > GetChildren ( RuleContext ruleCtx )
173
+ {
174
+ var result = new List < IParseTree > ( ) ;
175
+ for ( int index = 0 ; index < ruleCtx . ChildCount ; index ++ )
176
+ {
177
+ result . Add ( ruleCtx . GetChild ( index ) ) ;
178
+ }
179
+ return result ;
180
+ }
121
181
}
122
182
}
0 commit comments