Skip to content

Commit 4f4e2eb

Browse files
authored
Merge branch 'next' into typelibs-api-refactor
2 parents f606675 + 35ef70e commit 4f4e2eb

27 files changed

+737
-23
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Rubberduck.Inspections.Abstract;
2+
using Rubberduck.Inspections.Results;
3+
using Rubberduck.Parsing.Inspections.Abstract;
4+
using Rubberduck.Resources.Inspections;
5+
using Rubberduck.Parsing.VBA;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using Rubberduck.Parsing.Symbols;
9+
using Rubberduck.Inspections.Inspections.Extensions;
10+
using Rubberduck.Common;
11+
12+
namespace Rubberduck.Inspections.Concrete
13+
{
14+
/// <summary>
15+
/// Identifies empty module member blocks.
16+
/// </summary>
17+
/// <why>
18+
/// Methods containing no executable statements are misleading as they appear to be doing something which they actually don't.
19+
/// This might be the result of delaying the actual implementation for a later stage of development, and then forgetting all about that.
20+
/// </why>
21+
/// <example hasResults="true">
22+
/// <![CDATA[
23+
/// Sub Foo()
24+
/// ' ...
25+
/// End Sub
26+
/// ]]>
27+
/// </example>
28+
/// <example hasResults="false">
29+
/// <![CDATA[
30+
/// Sub Foo()
31+
/// MsgBox "?"
32+
/// End Sub
33+
/// ]]>
34+
/// </example>
35+
internal class EmptyMethodInspection : InspectionBase
36+
{
37+
public EmptyMethodInspection(RubberduckParserState state)
38+
: base(state) { }
39+
40+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
41+
{
42+
var allInterfaces = new HashSet<ClassModuleDeclaration>(State.DeclarationFinder.FindAllUserInterfaces());
43+
44+
return State.DeclarationFinder.UserDeclarations(DeclarationType.Member)
45+
.Where(member => !allInterfaces.Any(userInterface => userInterface.QualifiedModuleName == member.QualifiedModuleName)
46+
&& !member.IsIgnoringInspectionResultFor(AnnotationName)
47+
&& !((ModuleBodyElementDeclaration)member).Block.ContainsExecutableStatements())
48+
49+
.Select(result => new DeclarationInspectionResult(this,
50+
string.Format(InspectionResults.EmptyMethodInspection,
51+
Resources.RubberduckUI.ResourceManager
52+
.GetString("DeclarationType_" + result.DeclarationType)
53+
.Capitalize(),
54+
result.IdentifierName),
55+
result));
56+
}
57+
}
58+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using Rubberduck.Inspections.Abstract;
2+
using Rubberduck.Inspections.Results;
3+
using Rubberduck.Parsing.Inspections.Abstract;
4+
using Rubberduck.Resources.Inspections;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Rubberduck.Parsing.Symbols;
8+
using Rubberduck.Inspections.Inspections.Extensions;
9+
using Rubberduck.Common;
10+
11+
namespace Rubberduck.Inspections.Concrete
12+
{
13+
/// <summary>
14+
/// Identifies implemented members of class modules that are used as interfaces.
15+
/// </summary>
16+
/// <why>
17+
/// Interfaces provide a unified programmatic access to different objects, and therefore are rarely instantiated as concrete objects.
18+
/// </why>
19+
/// <example hasResults="false">
20+
/// <![CDATA[
21+
/// Sub Foo()
22+
/// ' ...
23+
/// End Sub
24+
/// ]]>
25+
/// </example>
26+
/// <example hasResults="true">
27+
/// <![CDATA[
28+
/// Sub Foo()
29+
/// MsgBox "?"
30+
/// End Sub
31+
/// ]]>
32+
/// </example>
33+
internal class ImplementedInterfaceMemberInspection : InspectionBase
34+
{
35+
public ImplementedInterfaceMemberInspection(Parsing.VBA.RubberduckParserState state)
36+
: base(state) { }
37+
38+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
39+
{
40+
return State.DeclarationFinder.FindAllUserInterfaces()
41+
.SelectMany(interfaceModule => interfaceModule.Members
42+
.Where(member => ((ModuleBodyElementDeclaration)member).Block.ContainsExecutableStatements(true)
43+
&& !member.IsIgnoringInspectionResultFor(AnnotationName)))
44+
.Select(result => new DeclarationInspectionResult(this,
45+
string.Format(InspectionResults.ImplementedInterfaceMemberInspection,
46+
Resources.RubberduckUI.ResourceManager
47+
.GetString("DeclarationType_" + result.DeclarationType)
48+
.Capitalize(),
49+
result.IdentifierName),
50+
result));
51+
}
52+
}
53+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using Antlr4.Runtime.Tree;
2+
using static Rubberduck.Parsing.Grammar.VBAParser;
3+
4+
namespace Rubberduck.Inspections.Inspections.Extensions
5+
{
6+
public static class ExecutableBlocksExtensions
7+
{
8+
/// <summary>
9+
/// Checks a block of code for executable statments and returns true if are present.
10+
/// </summary>
11+
/// <param name="block">The block to inspect</param>
12+
/// <param name="considerAllocations">Determines wheather Dim or Const statements should be considered executables</param>
13+
/// <returns></returns>
14+
public static bool ContainsExecutableStatements(this BlockContext block, bool considerAllocations = false)
15+
{
16+
return block?.children != null && ContainsExecutableStatements(block.children, considerAllocations);
17+
}
18+
19+
private static bool ContainsExecutableStatements(System.Collections.Generic.IList<IParseTree> blockChildren,
20+
bool considerAllocations = false)
21+
{
22+
foreach (var child in blockChildren)
23+
{
24+
if (child is BlockStmtContext blockStmt)
25+
{
26+
var mainBlockStmt = blockStmt.mainBlockStmt();
27+
28+
if (mainBlockStmt == null)
29+
{
30+
continue; //We have a lone line label, which is not executable.
31+
}
32+
33+
// if inspection does not consider allocations as executables,
34+
// exclude variables and consts because they are not executable statements
35+
if (!considerAllocations && IsConstOrVariable(mainBlockStmt.GetChild(0)))
36+
{
37+
continue;
38+
}
39+
40+
return true;
41+
}
42+
43+
if (child is RemCommentContext ||
44+
child is CommentContext ||
45+
child is CommentOrAnnotationContext ||
46+
child is EndOfStatementContext)
47+
{
48+
continue;
49+
}
50+
51+
return true;
52+
}
53+
54+
return false;
55+
}
56+
57+
private static bool IsConstOrVariable(IParseTree block)
58+
{
59+
return block is VariableStmtContext || block is ConstStmtContext;
60+
}
61+
}
62+
}

Rubberduck.CodeAnalysis/Rubberduck.CodeAnalysis.xml

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

Rubberduck.CodeAnalysis/app.config

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@
175175
Severity="Warning" InspectionType="CodeQualityIssues" />
176176
<CodeInspection Name="ExcelUdfNameIsValidCellReferenceInspection"
177177
Severity="Warning" InspectionType="CodeQualityIssues" />
178+
<CodeInspection Name="EmptyMethodInspection" Severity="Warning"
179+
InspectionType="CodeQualityIssues" />
180+
<CodeInspection Name="ImplementedInterfaceMemberInspection" Severity="Suggestion"
181+
InspectionType="CodeQualityIssues" />
178182
</CodeInspections>
179183
<WhitelistedIdentifiers />
180184
<RunInspectionsOnSuccessfulParse>true</RunInspectionsOnSuccessfulParse>
@@ -264,7 +268,9 @@
264268
&lt;CodeInspection Name="AssignmentNotUsedInspection" Severity="Suggestion" InspectionType="CodeQualityIssues" /&gt;
265269
&lt;CodeInspection Name="UnderscoreInPublicClassModuleMemberInspection" Severity="Warning" InspectionType="CodeQualityIssues" /&gt;
266270
&lt;CodeInspection Name="ExcelUdfNameIsValidCellReferenceInspection" Severity="Warning" InspectionType="CodeQualityIssues" /&gt;
267-
&lt;/CodeInspections&gt;
271+
&lt;CodeInspection Name="EmptyMethodInspection" Severity="Warning" InspectionType="CodeQualityIssues" /&gt;
272+
273+
&lt;/CodeInspections&gt;
268274
&lt;WhitelistedIdentifiers /&gt;
269275
&lt;RunInspectionsOnSuccessfulParse&gt;true&lt;/RunInspectionsOnSuccessfulParse&gt;
270276
&lt;/CodeInspectionSettings&gt;

Rubberduck.Core/UI/UnitTesting/TestExplorerControl.xaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,13 @@
5454
<BitmapImage x:Key="OutcomeInconclusive" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/exclamation.png" />
5555
<BitmapImage x:Key="OutcomeSucceeded" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/tick-circle.png" />
5656

57+
<BitmapImage x:Key="IgnoreTestImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/flask-empty.png" />
58+
<BitmapImage x:Key="UnignoreTestImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/flask-undo.png" />
59+
5760
<local:TestOutcomeImageSourceConverter x:Key="OutcomeIconConverter" />
5861
<converters:MillisecondToTimeMagnitudeConverter x:Key="FormattedTime" />
5962
<converters:InvertBoolValueConverter x:Key="InvertBoolValue" />
63+
<BooleanToVisibilityConverter x:Key="BoolToVisibility" />
6064

6165
<Style x:Key="ToolbarImageOpacity" TargetType="Image" >
6266
<Setter Property="Height" Value="16" />
@@ -433,6 +437,20 @@
433437
<Image Source="{StaticResource RunAllTestsImage}" />
434438
</MenuItem.Icon>
435439
</MenuItem>
440+
<MenuItem Command="{Binding IgnoreTestCommand}"
441+
Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_TestToggle_Ignore}"
442+
Visibility="{Binding DisplayIgnoreTestLabel, Converter={StaticResource BoolToVisibility}}">
443+
<MenuItem.Icon>
444+
<Image Source="{StaticResource IgnoreTestImage}" />
445+
</MenuItem.Icon>
446+
</MenuItem>
447+
<MenuItem Command="{Binding UnignoreTestCommand}"
448+
Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_TestToggle_Unignore}"
449+
Visibility="{Binding DisplayUnignoreTestLabel, Converter={StaticResource BoolToVisibility}}">
450+
<MenuItem.Icon>
451+
<Image Source="{StaticResource UnignoreTestImage}" />
452+
</MenuItem.Icon>
453+
</MenuItem>
436454
</ContextMenu>
437455
</DataGrid.ContextMenu>
438456
<i:Interaction.Behaviors>

0 commit comments

Comments
 (0)