Skip to content

Commit 2971896

Browse files
committed
Merge branch 'next' into SelectiveReferenceResolving
Incorporating recent changes.
2 parents 5550154 + 427ef60 commit 2971896

38 files changed

+1441
-952
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Antlr4.Runtime;
2+
using Rubberduck.Parsing.Symbols;
3+
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
4+
5+
namespace Rubberduck.Common
6+
{
7+
public static class CodeModuleExtensions
8+
{
9+
public static void ReplaceToken(this ICodeModule module, IToken token, string replacement)
10+
{
11+
var original = module.GetLines(token.Line, 1);
12+
var result = ReplaceStringAtIndex(original, token.Text, replacement, token.Column);
13+
module.ReplaceLine(token.Line, result);
14+
}
15+
16+
public static void ReplaceIdentifierReferenceName(this ICodeModule module, IdentifierReference identifierReference, string replacement)
17+
{
18+
var original = module.GetLines(identifierReference.Selection.StartLine, 1);
19+
var result = ReplaceStringAtIndex(original, identifierReference.IdentifierName, replacement, identifierReference.Context.Start.Column);
20+
module.ReplaceLine(identifierReference.Selection.StartLine, result);
21+
}
22+
23+
public static void InsertLines(this ICodeModule module, int startLine, string[] lines)
24+
{
25+
int lineNumber = startLine;
26+
for ( int idx = 0; idx < lines.Length; idx++ )
27+
{
28+
module.InsertLines(lineNumber, lines[idx]);
29+
lineNumber++;
30+
}
31+
}
32+
private static string ReplaceStringAtIndex(string original, string toReplace, string replacement, int startIndex)
33+
{
34+
var modifiedContent = original.Remove(startIndex, toReplace.Length);
35+
return modifiedContent.Insert(startIndex, replacement);
36+
}
37+
}
38+
}

RetailCoder.VBE/Inspections/Abstract/InspectionBase.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,28 +116,29 @@ protected bool IsIgnoringInspectionResultFor(IVBComponent component, int line)
116116

117117
protected bool IsIgnoringInspectionResultFor(Declaration declaration, string inspectionName)
118118
{
119+
var module = Declaration.GetModuleParent(declaration);
120+
if (module == null) { return false; }
121+
122+
var isIgnoredAtModuleLevel = module.Annotations
123+
.Any(annotation => annotation.AnnotationType == AnnotationType.IgnoreModule
124+
&& ((IgnoreModuleAnnotation) annotation).IsIgnored(inspectionName));
125+
126+
119127
if (declaration.DeclarationType == DeclarationType.Parameter)
120128
{
121-
return declaration.ParentDeclaration.Annotations.Any(annotation =>
129+
return isIgnoredAtModuleLevel || declaration.ParentDeclaration.Annotations.Any(annotation =>
122130
annotation.AnnotationType == AnnotationType.Ignore
123131
&& ((IgnoreAnnotation)annotation).IsIgnored(inspectionName));
124132
}
125133

126-
return declaration.Annotations.Any(annotation =>
134+
return isIgnoredAtModuleLevel || declaration.Annotations.Any(annotation =>
127135
annotation.AnnotationType == AnnotationType.Ignore
128136
&& ((IgnoreAnnotation)annotation).IsIgnored(inspectionName));
129137
}
130138

131139
protected bool IsIgnoringInspectionResultFor(IdentifierReference reference, string inspectionName)
132140
{
133-
if (reference == null)
134-
{
135-
return false;
136-
}
137-
138-
return reference.Annotations.Any(annotation =>
139-
annotation.AnnotationType == AnnotationType.Ignore
140-
&& ((IgnoreAnnotation)annotation).IsIgnored(inspectionName));
141+
return reference != null && reference.IsIgnoringInspectionResultFor(inspectionName);
141142
}
142143

143144
public int CompareTo(IInspection other)

RetailCoder.VBE/Inspections/AssignedByValParameterInspection.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@
33
using Rubberduck.Inspections.Abstract;
44
using Rubberduck.Inspections.Resources;
55
using Rubberduck.Inspections.Results;
6-
using Rubberduck.Parsing.Grammar;
76
using Rubberduck.Parsing.Symbols;
87
using Rubberduck.Parsing.VBA;
8+
using Rubberduck.UI.Refactorings;
99

1010
namespace Rubberduck.Inspections
1111
{
1212
public sealed class AssignedByValParameterInspection : InspectionBase
1313
{
14-
public AssignedByValParameterInspection(RubberduckParserState state)
14+
private readonly IAssignedByValParameterQuickFixDialogFactory _dialogFactory;
15+
public AssignedByValParameterInspection(RubberduckParserState state, IAssignedByValParameterQuickFixDialogFactory dialogFactory)
1516
: base(state)
1617
{
1718
Severity = DefaultSeverity;
19+
_dialogFactory = dialogFactory;
20+
1821
}
1922

2023
public override string Meta { get { return InspectionsUI.AssignedByValParameterInspectionMeta; } }
@@ -31,7 +34,7 @@ public override IEnumerable<InspectionResultBase> GetInspectionResults()
3134
.ToList();
3235

3336
return parameters
34-
.Select(param => new AssignedByValParameterInspectionResult(this, param))
37+
.Select(param => new AssignedByValParameterInspectionResult(this, param, _dialogFactory))
3538
.ToList();
3639
}
3740
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using Rubberduck.Inspections.Abstract;
2+
using System.Linq;
3+
using Rubberduck.VBEditor;
4+
using Rubberduck.Inspections.Resources;
5+
using Rubberduck.Parsing.Grammar;
6+
using Rubberduck.Parsing.Symbols;
7+
using System.Windows.Forms;
8+
using Rubberduck.UI.Refactorings;
9+
using Rubberduck.Common;
10+
using Antlr4.Runtime;
11+
using System.Collections.Generic;
12+
using Antlr4.Runtime.Tree;
13+
14+
namespace Rubberduck.Inspections.QuickFixes
15+
{
16+
public class AssignedByValParameterMakeLocalCopyQuickFix : QuickFixBase
17+
{
18+
private readonly Declaration _target;
19+
private readonly IAssignedByValParameterQuickFixDialogFactory _dialogFactory;
20+
private readonly IEnumerable<string> _forbiddenNames;
21+
private string _localCopyVariableName;
22+
23+
public AssignedByValParameterMakeLocalCopyQuickFix(Declaration target, QualifiedSelection selection, IAssignedByValParameterQuickFixDialogFactory dialogFactory)
24+
: base(target.Context, selection, InspectionsUI.AssignedByValParameterMakeLocalCopyQuickFix)
25+
{
26+
_target = target;
27+
_dialogFactory = dialogFactory;
28+
_forbiddenNames = GetIdentifierNamesAccessibleToProcedureContext(target.Context.Parent.Parent);
29+
_localCopyVariableName = ComputeSuggestedName();
30+
}
31+
32+
public override bool CanFixInModule { get { return false; } }
33+
public override bool CanFixInProject { get { return false; } }
34+
35+
public override void Fix()
36+
{
37+
RequestLocalCopyVariableName();
38+
39+
if (!VariableNameIsValid(_localCopyVariableName) || IsCancelled)
40+
{
41+
return;
42+
}
43+
44+
ReplaceAssignedByValParameterReferences();
45+
46+
InsertLocalVariableDeclarationAndAssignment();
47+
}
48+
49+
private void RequestLocalCopyVariableName()
50+
{
51+
using( var view = _dialogFactory.Create(_target.IdentifierName, _target.DeclarationType.ToString(), _forbiddenNames))
52+
{
53+
view.NewName = _localCopyVariableName;
54+
view.ShowDialog();
55+
IsCancelled = view.DialogResult == DialogResult.Cancel;
56+
if (!IsCancelled)
57+
{
58+
_localCopyVariableName = view.NewName;
59+
}
60+
}
61+
}
62+
63+
private string ComputeSuggestedName()
64+
{
65+
var newName = "local" + _target.IdentifierName.CapitalizeFirstLetter();
66+
if (VariableNameIsValid(newName))
67+
{
68+
return newName;
69+
}
70+
71+
for ( var attempt = 2; attempt < 10; attempt++)
72+
{
73+
var result = newName + attempt;
74+
if (VariableNameIsValid(result))
75+
{
76+
return result;
77+
}
78+
}
79+
return newName;
80+
}
81+
82+
private bool VariableNameIsValid(string variableName)
83+
{
84+
return VariableNameValidator.IsValidName(variableName)
85+
&& !_forbiddenNames.Any(name => name.Equals(variableName, System.StringComparison.InvariantCultureIgnoreCase));
86+
}
87+
88+
private void ReplaceAssignedByValParameterReferences()
89+
{
90+
var module = Selection.QualifiedName.Component.CodeModule;
91+
foreach (var identifierReference in _target.References)
92+
{
93+
module.ReplaceIdentifierReferenceName(identifierReference, _localCopyVariableName);
94+
}
95+
}
96+
97+
private void InsertLocalVariableDeclarationAndAssignment()
98+
{
99+
var block = QuickFixHelper.GetBlockStmtContextsForContext(_target.Context.Parent.Parent).FirstOrDefault();
100+
if (block == null)
101+
{
102+
return;
103+
}
104+
105+
string[] lines = { BuildLocalCopyDeclaration(), BuildLocalCopyAssignment() };
106+
var module = Selection.QualifiedName.Component.CodeModule;
107+
module.InsertLines(block.Start.Line, lines);
108+
}
109+
110+
private string BuildLocalCopyDeclaration()
111+
{
112+
return Tokens.Dim + " " + _localCopyVariableName + " " + Tokens.As + " " + _target.AsTypeName;
113+
}
114+
115+
private string BuildLocalCopyAssignment()
116+
{
117+
return (_target.AsTypeDeclaration is ClassModuleDeclaration ? Tokens.Set + " " : string.Empty)
118+
+ _localCopyVariableName + " = " + _target.IdentifierName;
119+
}
120+
121+
private IEnumerable<string> GetIdentifierNamesAccessibleToProcedureContext(RuleContext ruleContext)
122+
{
123+
var allIdentifiers = new HashSet<string>();
124+
125+
var blocks = QuickFixHelper.GetBlockStmtContextsForContext(ruleContext);
126+
127+
var blockStmtIdentifiers = GetIdentifierNames(blocks);
128+
allIdentifiers.UnionWith(blockStmtIdentifiers);
129+
130+
var args = QuickFixHelper.GetArgContextsForContext(ruleContext);
131+
132+
var potentiallyUnreferencedParameters = GetIdentifierNames(args);
133+
allIdentifiers.UnionWith(potentiallyUnreferencedParameters);
134+
135+
//TODO: add module and global scope variableNames to the list.
136+
137+
return allIdentifiers.ToArray();
138+
}
139+
140+
private IEnumerable<string> GetIdentifierNames(IEnumerable<RuleContext> ruleContexts)
141+
{
142+
var identifiers = new HashSet<string>();
143+
foreach (var identifiersForThisContext in ruleContexts.Select(GetIdentifierNames))
144+
{
145+
identifiers.UnionWith(identifiersForThisContext);
146+
}
147+
return identifiers;
148+
}
149+
150+
private static HashSet<string> GetIdentifierNames(RuleContext ruleContext)
151+
{
152+
// note: this looks like something that's already handled somewhere else...
153+
154+
//Recursively work through the tree to get all IdentifierContexts
155+
var results = new HashSet<string>();
156+
var tokenValues = typeof(Tokens).GetFields().Select(item => item.GetValue(null)).Cast<string>().Select(item => item).ToArray();
157+
var children = GetChildren(ruleContext);
158+
159+
foreach (var child in children)
160+
{
161+
var context = child as VBAParser.IdentifierContext;
162+
if (context != null)
163+
{
164+
var childName = Identifier.GetName(context);
165+
if (!tokenValues.Contains(childName))
166+
{
167+
results.Add(childName);
168+
}
169+
}
170+
else
171+
{
172+
if (!(child is TerminalNodeImpl))
173+
{
174+
results.UnionWith(GetIdentifierNames((RuleContext)child));
175+
}
176+
}
177+
}
178+
return results;
179+
}
180+
181+
private static IEnumerable<IParseTree> GetChildren(IParseTree tree)
182+
{
183+
var result = new List<IParseTree>();
184+
for (var index = 0; index < tree.ChildCount; index++)
185+
{
186+
result.Add(tree.GetChild(index));
187+
}
188+
return result;
189+
}
190+
}
191+
}

0 commit comments

Comments
 (0)