Skip to content

Commit fae5c6b

Browse files
committed
Adds IsMissingOnInappropriateArgumentInspection
1 parent 17303c2 commit fae5c6b

17 files changed

+410
-6
lines changed

Rubberduck.CodeAnalysis/Inspections/Abstract/QuickFixBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Rubberduck.Inspections.Abstract
1010
{
1111
public abstract class QuickFixBase : IQuickFix
1212
{
13-
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
13+
protected readonly ILogger Logger = LogManager.GetCurrentClassLogger();
1414
private HashSet<Type> _supportedInspections;
1515
public IReadOnlyCollection<Type> SupportedInspections => _supportedInspections.ToList();
1616

@@ -27,7 +27,7 @@ public void RegisterInspections(params Type[] inspections)
2727
throw new ArgumentException($"Parameters must implement {nameof(IInspection)}", nameof(inspections));
2828
#else
2929
inspections.Where(s => s.GetInterfaces().All(i => i != typeof(IInspection))).ToList()
30-
.ForEach(i => _logger.Error($"Type {i.Name} does not implement {nameof(IInspection)}"));
30+
.ForEach(i => Logger.Error($"Type {i.Name} does not implement {nameof(IInspection)}"));
3131
#endif
3232
}
3333

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Antlr4.Runtime;
4+
using NLog;
5+
using Rubberduck.Inspections.Abstract;
6+
using Rubberduck.Inspections.Results;
7+
using Rubberduck.Parsing;
8+
using Rubberduck.Parsing.Grammar;
9+
using Rubberduck.Parsing.Inspections.Abstract;
10+
using Rubberduck.Parsing.Symbols;
11+
using Rubberduck.Parsing.VBA;
12+
using Rubberduck.Resources.Inspections;
13+
14+
namespace Rubberduck.Inspections.Concrete
15+
{
16+
public class IsMissingOnInappropriateArgumentInspection : InspectionBase
17+
{
18+
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
19+
20+
public IsMissingOnInappropriateArgumentInspection(RubberduckParserState state)
21+
: base(state) { }
22+
23+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
24+
{
25+
var isMissing = BuiltInDeclarations.SingleOrDefault(decl => decl.QualifiedName.ToString().Equals("VBE7.DLL;VBA.Information.IsMissing"));
26+
27+
if (isMissing == null)
28+
{
29+
_logger.Trace("VBA.Information.IsMissing was not found in IsMissingOnInappropriateArgumentInspection.");
30+
return Enumerable.Empty<IInspectionResult>();
31+
}
32+
33+
var results = new List<IInspectionResult>();
34+
35+
foreach (var reference in isMissing.References.Where(candidate => !IsIgnoringInspectionResultFor(candidate, AnnotationName)))
36+
{
37+
// First case is for unqualified use: IsMissing(foo)
38+
// Second case if for use as a member access: VBA.IsMissing(foo)
39+
var argument = ((ParserRuleContext)reference.Context.Parent).GetDescendent<VBAParser.ArgumentExpressionContext>() ??
40+
((ParserRuleContext)reference.Context.Parent.Parent).GetDescendent<VBAParser.ArgumentExpressionContext>();
41+
var name = argument.GetDescendent<VBAParser.SimpleNameExprContext>();
42+
if (name.Parent.Parent != argument)
43+
{
44+
continue;
45+
}
46+
47+
var procedure = reference.Context.GetAncestor<VBAParser.ModuleBodyElementContext>();
48+
var parameter = UserDeclarations.Where(decl => decl is ModuleBodyElementDeclaration)
49+
.Cast<ModuleBodyElementDeclaration>()
50+
.FirstOrDefault(decl => decl.Context.Parent == procedure)?
51+
.Parameters.FirstOrDefault(param => param.IdentifierName.Equals(name.GetText()));
52+
53+
if (parameter == null || parameter.IsOptional && parameter.AsTypeName.Equals(Tokens.Variant) && string.IsNullOrEmpty(parameter.DefaultValue))
54+
{
55+
continue;
56+
}
57+
58+
results.Add(new IdentifierReferenceInspectionResult(this, InspectionResults.IsMissingOnInappropriateArgumentInspection, State, reference));
59+
}
60+
61+
return results;
62+
}
63+
}
64+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Windows.Forms;
7+
using NLog;
8+
using Rubberduck.Inspections.Abstract;
9+
using Rubberduck.Inspections.Concrete;
10+
using Rubberduck.Parsing.Grammar;
11+
using Rubberduck.Parsing.Inspections.Abstract;
12+
using Rubberduck.Parsing.Symbols;
13+
using Rubberduck.Parsing.VBA;
14+
15+
namespace Rubberduck.Inspections.QuickFixes
16+
{
17+
public class IsMissingOnInappropriateArgumentQuickFix : QuickFixBase
18+
{
19+
private readonly RubberduckParserState _state;
20+
21+
public IsMissingOnInappropriateArgumentQuickFix(RubberduckParserState state)
22+
: base(typeof(IsMissingOnInappropriateArgumentInspection))
23+
{
24+
_state = state;
25+
}
26+
27+
public override void Fix(IInspectionResult result)
28+
{
29+
if (!(result.Target is ParameterDeclaration parameter))
30+
{
31+
Logger.Trace(
32+
$"Target for IsMissingOnInappropriateArgumentQuickFix was {(result.Target == null ? "null" : "not a ParameterDeclaration")}.");
33+
return;
34+
}
35+
36+
var rewriter = _state.GetRewriter(result.QualifiedSelection.QualifiedName);
37+
38+
if (parameter.IsParamArray)
39+
{
40+
rewriter.Replace(result.Context, $"{Tokens.LBound}({parameter.IdentifierName}) > {Tokens.UBound}({parameter.IdentifierName})");
41+
}
42+
else if (!string.IsNullOrEmpty(parameter.DefaultValue))
43+
{
44+
if (parameter.DefaultValue.Equals("\"\""))
45+
{
46+
rewriter.Replace(result.Context, $"{parameter.IdentifierName} = {Tokens.vbNullString}");
47+
}
48+
49+
if (parameter.DefaultValue.Equals(Tokens.Nothing))
50+
{
51+
rewriter.Replace(result.Context, $"{parameter.IdentifierName} Is {Tokens.Nothing}");
52+
}
53+
54+
rewriter.Replace(result.Context, $"{parameter.IdentifierName} = {parameter.DefaultValue}");
55+
}
56+
}
57+
58+
public override string Description(IInspectionResult result) => Resources.Inspections.QuickFixes.IsMissingOnInappropriateArgumentQuickFix;
59+
60+
public override bool CanFixInProcedure => true;
61+
public override bool CanFixInModule => true;
62+
public override bool CanFixInProject => true;
63+
}
64+
}

Rubberduck.CodeAnalysis/Rubberduck.CodeAnalysis.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
<Compile Include="Inspections\Concrete\EmptyForEachBlockInspection.cs" />
8282
<Compile Include="Inspections\Concrete\EmptyForLoopBlockInspection.cs" />
8383
<Compile Include="Inspections\Concrete\BooleanAssignedInIfElseInspection.cs" />
84+
<Compile Include="Inspections\Concrete\IsMissingOnInappropriateArgumentInspection.cs" />
8485
<Compile Include="Inspections\Concrete\OnLocalErrorInspection.cs" />
8586
<Compile Include="Inspections\Concrete\ModuleWithoutFolderInspection.cs" />
8687
<Compile Include="Inspections\Concrete\EmptyWhileWendBlockInspection.cs" />
@@ -158,6 +159,7 @@
158159
<Compile Include="Inspections\Concrete\ProcedureNotUsedInspection.cs" />
159160
<Compile Include="Properties\AssemblyInfo.cs" />
160161
<Compile Include="QuickFixes\AccessSheetUsingCodeNameQuickFix.cs" />
162+
<Compile Include="QuickFixes\IsMissingOnInappropriateArgumentQuickFix.cs" />
161163
<Compile Include="QuickFixes\RemoveLocalErrorQuickFix.cs" />
162164
<Compile Include="QuickFixes\RemoveDuplicatedAnnotationQuickFix.cs" />
163165
<Compile Include="QuickFixes\ReplaceIfElseWithConditionalStatementQuickFix.cs" />

Rubberduck.Core/Properties/Settings.Designer.cs

Lines changed: 6 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rubberduck.Core/Properties/Settings.settings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@
281281
&lt;CodeInspection Name="DuplicatedAnnotationInspection" Severity="Error" InspectionType="RubberduckOpportunities" /&gt;
282282
&lt;CodeInspection Name="ModuleWithoutFolderInspection" Severity="Suggestion" InspectionType="RubberduckOpportunities" /&gt;
283283
&lt;CodeInspection Name="OnLocalErrorInspection" Severity="Suggestion" InspectionType="LanguageOpportunities" /&gt;
284+
&lt;CodeInspection Name="IsMissingOnInappropriateArgumentInspection" Severity="Warning" InspectionType="CodeQualityIssues" /&gt;
284285
&lt;/CodeInspections&gt;
285286
&lt;WhitelistedIdentifiers /&gt;
286287
&lt;RunInspectionsOnSuccessfulParse&gt;true&lt;/RunInspectionsOnSuccessfulParse&gt;

Rubberduck.Core/app.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@
406406
InspectionType="RubberduckOpportunities" />
407407
<CodeInspection Name="OnLocalErrorInspection" Severity="Suggestion"
408408
InspectionType="LanguageOpportunities" />
409+
<CodeInspection Name="IsMissingOnInappropriateArgumentInspection"
410+
Severity="Warning" InspectionType="CodeQualityIssues" />
409411
</CodeInspections>
410412
<WhitelistedIdentifiers />
411413
<RunInspectionsOnSuccessfulParse>true</RunInspectionsOnSuccessfulParse>

Rubberduck.Resources/Inspections/InspectionInfo.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rubberduck.Resources/Inspections/InspectionInfo.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,4 +340,7 @@ If the parameter can be null, ignore this inspection result; passing a null valu
340340
<data name="OnLocalErrorInspection" xml:space="preserve">
341341
<value>On Local Error exists only for compatibility with previous versions of Visual Basic, and all Errors are treated as Local regardless of the Error statement. Use of this keyword inaccurately gives the impression that there is a distinction between types of error handling when there is not.</value>
342342
</data>
343+
<data name="IsMissingOnInappropriateArgumentInspection" xml:space="preserve">
344+
<value>IsMissing is only intended to be called on optional arguments, and will only return correct results if the type of the argument is 'Variant' with no explicit default value. All other uses will return 'False'.</value>
345+
</data>
343346
</root>

Rubberduck.Resources/Inspections/InspectionNames.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)