Skip to content

Commit c0441e8

Browse files
authored
Merge pull request #3525 from MDoerner/EmptyModuleInspection
Introduces an inspection that locates empty modules.
2 parents e419d42 + f9412aa commit c0441e8

File tree

7 files changed

+510
-5
lines changed

7 files changed

+510
-5
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Rubberduck.Inspections.Abstract;
4+
using Rubberduck.Inspections.Results;
5+
using Rubberduck.Parsing.Grammar;
6+
using Rubberduck.Parsing.Inspections.Abstract;
7+
using Rubberduck.Parsing.Inspections.Resources;
8+
using Rubberduck.Parsing.Symbols;
9+
using Rubberduck.Parsing.VBA;
10+
using Rubberduck.VBEditor;
11+
using Rubberduck.VBEditor.SafeComWrappers;
12+
13+
namespace Rubberduck.Inspections.Concrete
14+
{
15+
16+
public sealed class EmptyModuleInspection : InspectionBase
17+
{
18+
private EmptyModuleVisitor _emptyModuleVisitor;
19+
20+
public EmptyModuleInspection(RubberduckParserState state,
21+
CodeInspectionSeverity defaultSeverity = CodeInspectionSeverity.Hint)
22+
: base(state, defaultSeverity)
23+
{
24+
_emptyModuleVisitor = new EmptyModuleVisitor();
25+
}
26+
27+
public override CodeInspectionType InspectionType => CodeInspectionType.MaintainabilityAndReadabilityIssues;
28+
29+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
30+
{
31+
var modulesToInspect = State.DeclarationFinder.AllModules
32+
.Where(qmn => qmn.ComponentType == ComponentType.ClassModule
33+
|| qmn.ComponentType == ComponentType.StandardModule).ToHashSet();
34+
35+
var treesToInspect = State.ParseTrees.Where(kvp => modulesToInspect.Contains(kvp.Key));
36+
37+
var emptyModules = treesToInspect
38+
.Where(kvp => _emptyModuleVisitor.Visit(kvp.Value))
39+
.Select(kvp => kvp.Key)
40+
.ToHashSet();
41+
42+
var emptyModuleDeclarations = State.DeclarationFinder.UserDeclarations(DeclarationType.Module)
43+
.Where(declaration => emptyModules.Contains(declaration.QualifiedName.QualifiedModuleName)
44+
&& !IsIgnoringInspectionResultFor(declaration, AnnotationName));
45+
46+
return emptyModuleDeclarations.Select(declaration =>
47+
new DeclarationInspectionResult(this, string.Format(InspectionsUI.EmptyModuleInspectionResultFormat, declaration.IdentifierName), declaration));
48+
}
49+
}
50+
51+
internal sealed class EmptyModuleVisitor : VBAParserBaseVisitor<bool>
52+
{
53+
//If not specified otherwise, any context makes a module non-empty.
54+
protected override bool DefaultResult => false;
55+
56+
protected override bool AggregateResult(bool aggregate, bool nextResult)
57+
{
58+
return aggregate && nextResult;
59+
}
60+
61+
//We bail out whenever we already know that the module is non-empty.
62+
protected override bool ShouldVisitNextChild(Antlr4.Runtime.Tree.IRuleNode node, bool currentResult)
63+
{
64+
return currentResult;
65+
}
66+
67+
68+
public override bool VisitStartRule(VBAParser.StartRuleContext context)
69+
{
70+
return Visit(context.module());
71+
}
72+
73+
public override bool VisitModule(VBAParser.ModuleContext context)
74+
{
75+
return context.moduleConfig() == null
76+
&& Visit(context.moduleBody())
77+
&& Visit(context.moduleDeclarations());
78+
}
79+
80+
public override bool VisitModuleBody(VBAParser.ModuleBodyContext context)
81+
{
82+
return !context.moduleBodyElement().Any();
83+
}
84+
85+
public override bool VisitModuleDeclarations(VBAParser.ModuleDeclarationsContext context)
86+
{
87+
return !context.moduleDeclarationsElement().Any()
88+
|| context.moduleDeclarationsElement().All(Visit);
89+
}
90+
91+
public override bool VisitModuleDeclarationsElement(VBAParser.ModuleDeclarationsElementContext context)
92+
{
93+
return context.variableStmt() == null
94+
&& context.constStmt() == null
95+
&& context.enumerationStmt() == null
96+
&& context.privateTypeDeclaration() == null
97+
&& context.publicTypeDeclaration() == null
98+
&& context.eventStmt() == null
99+
&& context.implementsStmt() == null
100+
&& context.declareStmt() == null;
101+
}
102+
}
103+
}

Rubberduck.Inspections/Rubberduck.Inspections.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
<Compile Include="Abstract\ParseTreeInspectionBase.cs" />
6363
<Compile Include="Concrete\ApplicationWorksheetFunctionInspection.cs" />
6464
<Compile Include="Concrete\AssignedByValParameterInspection.cs" />
65+
<Compile Include="Concrete\EmptyModuleInspection.cs" />
6566
<Compile Include="Concrete\EmptyBlockInspectionListenerBase.cs" />
6667
<Compile Include="Concrete\EmptyCaseBlockInspection.cs" />
6768
<Compile Include="Concrete\EmptyDoWhileBlockInspection.cs" />

Rubberduck.Parsing/Inspections/Resources/InspectionsUI.de.resx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="UTF-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<root>
33
<!--
44
Microsoft ResX Schema
@@ -130,7 +130,7 @@
130130
<value>Konstante wird nicht verwendet</value>
131131
</data>
132132
<data name="DefaultProjectNameInspectionMeta" xml:space="preserve">
133-
<value>Erwäge dem VBA-Projekt einen Namen zu egeben.</value>
133+
<value>Erwäge dem VBA-Projekt einen Namen zu geben.</value>
134134
</data>
135135
<data name="DefaultProjectNameInspectionName" xml:space="preserve">
136136
<value>Das Projekt hat den Standard-Projektnamen.</value>
@@ -289,7 +289,7 @@
289289
<value>Variable wird genutzt ohne das ihr ein Wert zugewiesen wurde.</value>
290290
</data>
291291
<data name="UntypedFunctionUsageInspectionMeta" xml:space="preserve">
292-
<value>Eine gibt eine Funktion, die einen String Äquivalent zurückgibt. Diese sollte bevorzugt genutzt werden, um implitizite Typumwandlungen zu vermeiden.
292+
<value>Es gibt eine äquivalente Funktion, die einen String zurückgibt. Diese sollte bevorzugt genutzt werden, um implitizite Typumwandlungen zu vermeiden.
293293
Falls der Parameter 'null' sein kann, bitte dieses Auftreten ignorieren. 'null' an die Funktion zu übergeben, die einen String erwartet würde zu einem "Type Mismatch"-Laufzeitfehler führen.</value>
294294
</data>
295295
<data name="UntypedFunctionUsageInspectionName" xml:space="preserve">
@@ -867,4 +867,13 @@ Falls der Parameter 'null' sein kann, bitte dieses Auftreten ignorieren. 'null'
867867
<data name="BooleanAssignedInIfElseInspectionResultFormat" xml:space="preserve">
868868
<value>Boolean Ausdruck '{0}' wurde in einer trivialen If/Else-Verzweigung zugewiesen</value>
869869
</data>
870-
</root>
870+
<data name="EmptyModuleInspectionName" xml:space="preserve">
871+
<value>Leeres Modul</value>
872+
</data>
873+
<data name="EmptyModuleInspectionMeta" xml:space="preserve">
874+
<value>Leere Module und Klassen weisen entweder auf noch nicht implementierte Funktionalitäten hin oder stellen unnötigen Ballast dar, der die Wartbarkeit eines Projekts behindern kann.</value>
875+
</data>
876+
<data name="EmptyModuleInspectionResultFormat" xml:space="preserve">
877+
<value>Modul/Klasse {0} ist leer.</value>
878+
</data>
879+
</root>

Rubberduck.Parsing/Inspections/Resources/InspectionsUI.resx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<root>
33
<!--
44
Microsoft ResX Schema
@@ -878,4 +878,13 @@ If the parameter can be null, ignore this inspection result; passing a null valu
878878
<data name="ReplaceIfElseWithConditionalStatementQuickFix" xml:space="preserve">
879879
<value>Replace If/Else with single assignment</value>
880880
</data>
881+
<data name="EmptyModuleInspectionName" xml:space="preserve">
882+
<value>Empty module</value>
883+
</data>
884+
<data name="EmptyModuleInspectionMeta" xml:space="preserve">
885+
<value>Empty modules and classes either point to not yet implemented functionality or represent unnecessary baggage that can hurt the maintainability of a project.</value>
886+
</data>
887+
<data name="EmptyModuleInspectionResultFormat" xml:space="preserve">
888+
<value>Module/class {0} is empty.</value>
889+
</data>
881890
</root>

Rubberduck.Parsing/Inspections/Resources/InspectionsUI1.Designer.cs

Lines changed: 27 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)