Skip to content

Commit 8c3f788

Browse files
committed
Finish IsMissingOnInappropriateArgumentQuickFix.
1 parent fae5c6b commit 8c3f788

File tree

5 files changed

+277
-22
lines changed

5 files changed

+277
-22
lines changed

Rubberduck.CodeAnalysis/Inspections/Concrete/IsMissingOnInappropriateArgumentInspection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
5555
continue;
5656
}
5757

58-
results.Add(new IdentifierReferenceInspectionResult(this, InspectionResults.IsMissingOnInappropriateArgumentInspection, State, reference));
58+
results.Add(new IdentifierReferenceInspectionResult(this, InspectionResults.IsMissingOnInappropriateArgumentInspection, State, reference, parameter));
5959
}
6060

6161
return results;
Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
6-
using System.Windows.Forms;
7-
using NLog;
84
using Rubberduck.Inspections.Abstract;
95
using Rubberduck.Inspections.Concrete;
6+
using Rubberduck.Parsing;
107
using Rubberduck.Parsing.Grammar;
118
using Rubberduck.Parsing.Inspections.Abstract;
129
using Rubberduck.Parsing.Symbols;
@@ -26,39 +23,105 @@ public IsMissingOnInappropriateArgumentQuickFix(RubberduckParserState state)
2623

2724
public override void Fix(IInspectionResult result)
2825
{
29-
if (!(result.Target is ParameterDeclaration parameter))
26+
if (!(result.Properties is ParameterDeclaration parameter))
3027
{
3128
Logger.Trace(
32-
$"Target for IsMissingOnInappropriateArgumentQuickFix was {(result.Target == null ? "null" : "not a ParameterDeclaration")}.");
29+
$"Properties for IsMissingOnInappropriateArgumentQuickFix was {(result.Properties == null ? "null" : "not a ParameterDeclaration")}.");
3330
return;
3431
}
3532

3633
var rewriter = _state.GetRewriter(result.QualifiedSelection.QualifiedName);
34+
if (!result.Context.TryGetAncestor<VBAParser.LExprContext>(out var context))
35+
{
36+
Logger.Trace("IsMissingOnInappropriateArgumentQuickFix could not locate containing LExprContext for replacement.");
37+
return;
38+
}
3739

38-
if (parameter.IsParamArray)
40+
if (parameter.IsParamArray || parameter.IsArray)
3941
{
40-
rewriter.Replace(result.Context, $"{Tokens.LBound}({parameter.IdentifierName}) > {Tokens.UBound}({parameter.IdentifierName})");
42+
rewriter.Replace(context, $"{Tokens.LBound}({parameter.IdentifierName}) > {Tokens.UBound}({parameter.IdentifierName})");
43+
return;
4144
}
42-
else if (!string.IsNullOrEmpty(parameter.DefaultValue))
45+
46+
if (!string.IsNullOrEmpty(parameter.DefaultValue))
4347
{
4448
if (parameter.DefaultValue.Equals("\"\""))
4549
{
46-
rewriter.Replace(result.Context, $"{parameter.IdentifierName} = {Tokens.vbNullString}");
50+
rewriter.Replace(context, $"{parameter.IdentifierName} = {Tokens.vbNullString}");
4751
}
48-
49-
if (parameter.DefaultValue.Equals(Tokens.Nothing))
52+
else if (parameter.DefaultValue.Equals(Tokens.Nothing, StringComparison.InvariantCultureIgnoreCase))
53+
{
54+
rewriter.Replace(context, $"{parameter.IdentifierName} Is {Tokens.Nothing}");
55+
}
56+
else
5057
{
51-
rewriter.Replace(result.Context, $"{parameter.IdentifierName} Is {Tokens.Nothing}");
58+
rewriter.Replace(context, $"{parameter.IdentifierName} = {parameter.DefaultValue}");
5259
}
60+
return;
61+
}
62+
rewriter.Replace(context, UninitializedComparisonForParameter(parameter));
63+
}
5364

54-
rewriter.Replace(result.Context, $"{parameter.IdentifierName} = {parameter.DefaultValue}");
65+
private static readonly Dictionary<string, string> BaseTypeUninitializedValues = new Dictionary<string, string>
66+
{
67+
{ Tokens.Boolean.ToUpper(), Tokens.False },
68+
{ Tokens.Byte.ToUpper(), "0" },
69+
{ Tokens.Currency.ToUpper(), "0" },
70+
{ Tokens.Date.ToUpper(), "CDate(0)" },
71+
{ Tokens.Decimal.ToUpper(), "0" },
72+
{ Tokens.Double.ToUpper(), "0" },
73+
{ Tokens.Integer.ToUpper(), "0" },
74+
{ Tokens.Long.ToUpper(), "0" },
75+
{ Tokens.LongLong.ToUpper(), "0" },
76+
{ Tokens.LongPtr.ToUpper(), "0" },
77+
{ Tokens.Single.ToUpper(), "0" },
78+
{ Tokens.String.ToUpper(), Tokens.vbNullString }
79+
};
80+
81+
private string UninitializedComparisonForParameter(ParameterDeclaration parameter)
82+
{
83+
var type = parameter.AsTypeName?.ToUpper() ?? string.Empty;
84+
if (string.IsNullOrEmpty(type))
85+
{
86+
type = parameter.HasTypeHint
87+
? SymbolList.TypeHintToTypeName[parameter.TypeHint].ToUpper()
88+
: Tokens.Variant.ToUpper();
89+
}
90+
91+
if (BaseTypeUninitializedValues.ContainsKey(type))
92+
{
93+
return $"{parameter.IdentifierName} = {BaseTypeUninitializedValues[type]}";
94+
}
95+
96+
if (type.Equals(Tokens.Object, StringComparison.InvariantCultureIgnoreCase))
97+
{
98+
return $"{parameter.IdentifierName} Is {Tokens.Nothing}";
99+
}
100+
101+
if (type.Equals(Tokens.Object, StringComparison.InvariantCultureIgnoreCase) || parameter.AsTypeDeclaration == null)
102+
{
103+
return $"IsEmpty({parameter.IdentifierName})";
104+
}
105+
106+
switch (parameter.AsTypeDeclaration.DeclarationType)
107+
{
108+
case DeclarationType.ClassModule:
109+
return $"{parameter.IdentifierName} Is {Tokens.Nothing}";
110+
case DeclarationType.Enumeration:
111+
var members = _state.DeclarationFinder.AllDeclarations.OfType<ValuedDeclaration>()
112+
.FirstOrDefault(decl =>
113+
ReferenceEquals(decl.ParentDeclaration, parameter.AsTypeDeclaration) &&
114+
decl.Expression.Equals("0"));
115+
return $"{parameter.IdentifierName} = {members?.IdentifierName ?? "0"}";
116+
default:
117+
return $"IsError({parameter.IdentifierName})";
55118
}
56119
}
57120

58121
public override string Description(IInspectionResult result) => Resources.Inspections.QuickFixes.IsMissingOnInappropriateArgumentQuickFix;
59122

60123
public override bool CanFixInProcedure => true;
61-
public override bool CanFixInModule => true;
62-
public override bool CanFixInProject => true;
124+
public override bool CanFixInModule => false;
125+
public override bool CanFixInProject => false;
63126
}
64127
}

RubberduckTests/Inspections/IsMissingOnInappropriateArgumentInspectionTests.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
1+
using System.Linq;
52
using System.Threading;
6-
using System.Threading.Tasks;
73
using NUnit.Framework;
84
using Rubberduck.Inspections.Concrete;
95
using Rubberduck.VBEditor.SafeComWrappers;
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using NUnit.Framework;
9+
using Rubberduck.Inspections.Concrete;
10+
using Rubberduck.Inspections.QuickFixes;
11+
using Rubberduck.VBEditor.SafeComWrappers;
12+
using RubberduckTests.Inspections;
13+
using RubberduckTests.Mocks;
14+
15+
namespace RubberduckTests.QuickFixes
16+
{
17+
[TestFixture]
18+
public class IsMissingOnInappropriateArgumentQuickFixTests
19+
{
20+
[Test]
21+
[Category("QuickFixes")]
22+
public void OptionalStringArgument_QuickFixWorks()
23+
{
24+
const string inputCode =
25+
@"
26+
Public Sub Foo(Optional bar As String)
27+
Debug.Print IsMissing(bar)
28+
End Sub
29+
";
30+
31+
const string expected =
32+
@"
33+
Public Sub Foo(Optional bar As String)
34+
Debug.Print bar = vbNullString
35+
End Sub
36+
";
37+
38+
var actual = ArrangeAndApplyQuickFix(inputCode);
39+
Assert.AreEqual(expected, actual);
40+
}
41+
42+
[Test]
43+
[Category("QuickFixes")]
44+
public void OptionalStringArgumentFullyQualified_QuickFixWorks()
45+
{
46+
const string inputCode =
47+
@"
48+
Public Sub Foo(Optional bar As String)
49+
Debug.Print VBA.Information.IsMissing(bar)
50+
End Sub
51+
";
52+
53+
const string expected =
54+
@"
55+
Public Sub Foo(Optional bar As String)
56+
Debug.Print bar = vbNullString
57+
End Sub
58+
";
59+
60+
var actual = ArrangeAndApplyQuickFix(inputCode);
61+
Assert.AreEqual(expected, actual);
62+
}
63+
64+
[Test]
65+
[Category("QuickFixes")]
66+
public void OptionalStringArgumentPartiallyQualified_QuickFixWorks()
67+
{
68+
const string inputCode =
69+
@"
70+
Public Sub Foo(Optional bar As String)
71+
Debug.Print VBA.IsMissing(bar)
72+
End Sub
73+
";
74+
75+
const string expected =
76+
@"
77+
Public Sub Foo(Optional bar As String)
78+
Debug.Print bar = vbNullString
79+
End Sub
80+
";
81+
82+
var actual = ArrangeAndApplyQuickFix(inputCode);
83+
Assert.AreEqual(expected, actual);
84+
}
85+
86+
[Test]
87+
[Category("QuickFixes")]
88+
public void OptionalStringArgumentWithDefault_QuickFixWorks()
89+
{
90+
const string inputCode =
91+
@"
92+
Public Sub Foo(Optional bar As Variant = 42)
93+
Debug.Print IsMissing(bar)
94+
End Sub
95+
";
96+
97+
const string expected =
98+
@"
99+
Public Sub Foo(Optional bar As Variant = 42)
100+
Debug.Print bar = 42
101+
End Sub
102+
";
103+
104+
var actual = ArrangeAndApplyQuickFix(inputCode);
105+
Assert.AreEqual(expected, actual);
106+
}
107+
108+
[Test]
109+
[Category("QuickFixes")]
110+
public void ParamArray_QuickFixWorks()
111+
{
112+
const string inputCode =
113+
@"
114+
Public Sub Foo(ParamArray bar() As Variant)
115+
Debug.Print IsMissing(bar)
116+
End Sub
117+
";
118+
119+
const string expected =
120+
@"
121+
Public Sub Foo(ParamArray bar() As Variant)
122+
Debug.Print LBound(bar) > UBound(bar)
123+
End Sub
124+
";
125+
126+
var actual = ArrangeAndApplyQuickFix(inputCode);
127+
Assert.AreEqual(expected, actual);
128+
}
129+
130+
[Test]
131+
[Category("QuickFixes")]
132+
public void OptionalStringArgumentDefaultDoubleQuotes_QuickFixWorks()
133+
{
134+
const string inputCode =
135+
@"
136+
Public Sub Foo(Optional bar As String = """")
137+
Debug.Print IsMissing(bar)
138+
End Sub
139+
";
140+
141+
const string expected =
142+
@"
143+
Public Sub Foo(Optional bar As String = """")
144+
Debug.Print bar = vbNullString
145+
End Sub
146+
";
147+
148+
var actual = ArrangeAndApplyQuickFix(inputCode);
149+
Assert.AreEqual(expected, actual);
150+
}
151+
152+
[Test]
153+
[Category("QuickFixes")]
154+
public void ReferenceType_QuickFixWorks()
155+
{
156+
const string inputCode =
157+
@"
158+
Public Sub Foo(bar As Collection)
159+
Debug.Print IsMissing(bar)
160+
End Sub
161+
";
162+
163+
const string expected =
164+
@"
165+
Public Sub Foo(bar As Collection)
166+
Debug.Print bar Is Nothing
167+
End Sub
168+
";
169+
170+
var actual = ArrangeAndApplyQuickFix(inputCode);
171+
Assert.AreEqual(expected, actual);
172+
}
173+
174+
private string ArrangeAndApplyQuickFix(string code)
175+
{
176+
var builder = new MockVbeBuilder();
177+
var project = builder.ProjectBuilder("TestProject1", "TestProject1", ProjectProtection.Unprotected)
178+
.AddComponent("Module1", ComponentType.StandardModule, code)
179+
.AddReference("VBA", MockVbeBuilder.LibraryPathVBA, 4, 2, true)
180+
.Build();
181+
var vbe = builder.AddProject(project).Build();
182+
var component = project.Object.VBComponents.FirstOrDefault();
183+
184+
using (var state = MockParser.CreateAndParse(vbe.Object))
185+
{
186+
var inspection = new IsMissingOnInappropriateArgumentInspection(state);
187+
var inspector = InspectionsHelper.GetInspector(inspection);
188+
var inspectionResults = inspector.FindIssuesAsync(state, CancellationToken.None).Result;
189+
190+
new IsMissingOnInappropriateArgumentQuickFix(state).Fix(inspectionResults.First());
191+
return state.GetRewriter(component).GetText();
192+
}
193+
}
194+
}
195+
}

RubberduckTests/RubberduckTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
<Compile Include="PreProcessing\CompilationArgumentsCacheTests.cs" />
135135
<Compile Include="QuickFixes\AccessSheetUsingCodeNameQuickFixTests.cs" />
136136
<Compile Include="QuickFixes\IntroduceLocalVariableQuickFixTests.cs" />
137+
<Compile Include="QuickFixes\IsMissingOnInappropriateArgumentQuickFixTests.cs" />
137138
<Compile Include="QuickFixes\RemoveDuplicatedAnnotationQuickFixTests.cs" />
138139
<Compile Include="QuickFixes\RemoveLocalErrorQuickFixTests.cs" />
139140
<Compile Include="Rewriter\ArgumentRewriterInfoFinderTests.cs" />

0 commit comments

Comments
 (0)