Skip to content

Commit 38c928e

Browse files
authored
Merge pull request #5146 from MDoerner/FailedLetCoercionOnObjectInspection
New Inspections for failed default member resolutions
2 parents 225b09b + a39376f commit 38c928e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+4474
-331
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Rubberduck.Inspections.Abstract;
4+
using Rubberduck.Inspections.Inspections.Extensions;
5+
using Rubberduck.Inspections.Results;
6+
using Rubberduck.Parsing.Inspections;
7+
using Rubberduck.Parsing.Inspections.Abstract;
8+
using Rubberduck.Parsing.Symbols;
9+
using Rubberduck.Parsing.VBA;
10+
using Rubberduck.Resources.Inspections;
11+
12+
namespace Rubberduck.CodeAnalysis.Inspections.Concrete
13+
{
14+
/// <summary>
15+
/// Locates indexed default member calls for which the corresponding object does not have a suitable suitable default member.
16+
/// </summary>
17+
/// <why>
18+
/// The VBA compiler does not check whether the necessary default member is present. Instead there is a runtime error whenever the runtime type fails to have the default member.
19+
/// </why>
20+
/// <example hasResult="true">
21+
/// <![CDATA[
22+
/// Class1:
23+
///
24+
/// Public Function Foo(index As Long) As Long
25+
/// 'No default member attribute
26+
/// End Function
27+
///
28+
/// ------------------------------
29+
/// Module1:
30+
///
31+
/// Public Sub DoIt()
32+
/// Dim cls As Class1
33+
/// Dim bar As Variant
34+
/// Set cls = New Class1
35+
/// bar = cls(0)
36+
/// End Sub
37+
/// ]]>
38+
/// </example>
39+
/// <example hasResult="false">
40+
/// <![CDATA[
41+
/// Class1:
42+
///
43+
/// Public Function Foo(index As Long) As Long
44+
/// Attribute Foo.UserMemId = 0
45+
/// End Function
46+
///
47+
/// ------------------------------
48+
/// Module1:
49+
///
50+
/// Public Sub DoIt()
51+
/// Dim cls As Class1
52+
/// Dim bar As Variant
53+
/// Set cls = New Class1
54+
/// bar = cls(0)
55+
/// End Sub
56+
/// ]]>
57+
/// </example>
58+
public class DefaultMemberRequiredInspection : InspectionBase
59+
{
60+
private readonly IDeclarationFinderProvider _declarationFinderProvider;
61+
62+
public DefaultMemberRequiredInspection(RubberduckParserState state)
63+
: base(state)
64+
{
65+
_declarationFinderProvider = state;
66+
67+
//This will most likely cause a runtime error. The exceptions are rare and should be refactored or made explicit with an @Ignore annotation.
68+
Severity = CodeInspectionSeverity.Error;
69+
}
70+
71+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
72+
{
73+
var finder = _declarationFinderProvider.DeclarationFinder;
74+
75+
var failedIndexedDefaultMemberAccesses = finder.FailedIndexedDefaultMemberAccesses();
76+
return failedIndexedDefaultMemberAccesses
77+
.Where(failedIndexedDefaultMemberAccess => !IsIgnored(failedIndexedDefaultMemberAccess))
78+
.Select(failedIndexedDefaultMemberAccess => InspectionResult(failedIndexedDefaultMemberAccess, _declarationFinderProvider));
79+
}
80+
81+
private bool IsIgnored(IdentifierReference assignment)
82+
{
83+
return assignment.IsIgnoringInspectionResultFor(AnnotationName);
84+
}
85+
86+
private IInspectionResult InspectionResult(IdentifierReference failedCoercion, IDeclarationFinderProvider declarationFinderProvider)
87+
{
88+
return new IdentifierReferenceInspectionResult(this,
89+
ResultDescription(failedCoercion),
90+
declarationFinderProvider,
91+
failedCoercion);
92+
}
93+
94+
private string ResultDescription(IdentifierReference failedIndexedDefaultMemberAccess)
95+
{
96+
var expression = failedIndexedDefaultMemberAccess.IdentifierName;
97+
var typeName = failedIndexedDefaultMemberAccess.Declaration?.FullAsTypeName;
98+
return string.Format(InspectionResults.DefaultMemberRequiredInspection, expression, typeName);
99+
}
100+
}
101+
}

Rubberduck.CodeAnalysis/Inspections/Concrete/MemberNotOnInterfaceInspection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public MemberNotOnInterfaceInspection(RubberduckParserState state)
4444

4545
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
4646
{
47-
var unresolved = State.DeclarationFinder.UnresolvedMemberDeclarations
47+
var unresolved = State.DeclarationFinder.UnresolvedMemberDeclarations()
4848
.Where(decl => !decl.IsIgnoringInspectionResultFor(AnnotationName)).ToList();
4949

5050
var targets = Declarations.Where(decl => decl.AsTypeDeclaration != null &&

Rubberduck.CodeAnalysis/Inspections/Concrete/ObjectVariableNotSetInspection.cs

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
4+
using Antlr4.Runtime;
35
using Rubberduck.Inspections.Abstract;
46
using Rubberduck.Inspections.Results;
57
using Rubberduck.Parsing.Inspections.Abstract;
68
using Rubberduck.Resources.Inspections;
79
using Rubberduck.Parsing.Symbols;
810
using Rubberduck.Parsing.VBA;
911
using Rubberduck.Inspections.Inspections.Extensions;
12+
using Rubberduck.Parsing;
13+
using Rubberduck.Parsing.Grammar;
14+
using Rubberduck.Parsing.VBA.DeclarationCaching;
15+
using Rubberduck.VBEditor;
1016

1117
namespace Rubberduck.Inspections.Concrete
1218
{
@@ -42,30 +48,78 @@ public ObjectVariableNotSetInspection(RubberduckParserState state)
4248

4349
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
4450
{
51+
var finder = State.DeclarationFinder;
4552

46-
return InterestingReferences().Select(reference =>
47-
new IdentifierReferenceInspectionResult(this,
48-
string.Format(InspectionResults.ObjectVariableNotSetInspection, reference.Declaration.IdentifierName),
49-
State, reference));
53+
var failedLetResolutionResults = FailedLetResolutionResults(finder);
54+
55+
return failedLetResolutionResults
56+
.Select(reference =>
57+
new IdentifierReferenceInspectionResult(
58+
this,
59+
string.Format(InspectionResults.ObjectVariableNotSetInspection, reference.IdentifierName),
60+
State,
61+
reference));
5062
}
5163

52-
private IEnumerable<IdentifierReference> InterestingReferences()
64+
private IEnumerable<IdentifierReference> FailedLetResolutionResults(DeclarationFinder finder)
5365
{
54-
var result = new List<IdentifierReference>();
55-
foreach (var moduleReferences in State.DeclarationFinder.IdentifierReferences())
66+
var results = new List<IdentifierReference>();
67+
foreach (var moduleDeclaration in finder.UserDeclarations(DeclarationType.Module))
5668
{
57-
var module = State.DeclarationFinder.ModuleDeclaration(moduleReferences.Key);
58-
if (module == null || !module.IsUserDefined || module.IsIgnoringInspectionResultFor(AnnotationName))
69+
if (moduleDeclaration == null || moduleDeclaration.IsIgnoringInspectionResultFor(AnnotationName))
5970
{
60-
// module isn't user code (?), or this inspection is ignored at module-level
6171
continue;
6272
}
6373

64-
result.AddRange(moduleReferences.Value.Where(reference => !reference.IsSetAssignment
65-
&& VariableRequiresSetAssignmentEvaluator.RequiresSetAssignment(reference, State)));
74+
var module = moduleDeclaration.QualifiedModuleName;
75+
var failedLetCoercionAssignmentsInModule = FailedLetResolutionAssignments(module, finder);
76+
var possiblyObjectLhsLetAssignmentsWithFailedLetResolutionOnRhs = PossiblyObjectLhsLetAssignmentsWithNonValueOnRhs(module, finder);
77+
results.AddRange(failedLetCoercionAssignmentsInModule);
78+
results.AddRange(possiblyObjectLhsLetAssignmentsWithFailedLetResolutionOnRhs);
6679
}
6780

68-
return result.Where(reference => !reference.IsIgnoringInspectionResultFor(AnnotationName));
81+
return results.Where(reference => !reference.IsIgnoringInspectionResultFor(AnnotationName));
82+
}
83+
84+
private static IEnumerable<IdentifierReference> FailedLetResolutionAssignments(QualifiedModuleName module, DeclarationFinder finder)
85+
{
86+
return finder.FailedLetCoercions(module)
87+
.Where(reference => reference.IsAssignment);
88+
}
89+
90+
private static IEnumerable<IdentifierReference> PossiblyObjectLhsLetAssignmentsWithNonValueOnRhs(QualifiedModuleName module, DeclarationFinder finder)
91+
{
92+
return PossiblyObjectLhsLetAssignments(module, finder)
93+
.Where(tpl => finder.FailedLetCoercions(module)
94+
.Any(reference => reference.Selection.Equals(tpl.rhs.GetSelection()))
95+
|| Tokens.Nothing.Equals(tpl.rhs.GetText(), StringComparison.InvariantCultureIgnoreCase))
96+
.Select(tpl => tpl.assignment);
97+
}
98+
99+
private static IEnumerable<(IdentifierReference assignment, ParserRuleContext rhs)> PossiblyObjectLhsLetAssignments(QualifiedModuleName module, DeclarationFinder finder)
100+
{
101+
return PossiblyObjectNonSetAssignments(module, finder)
102+
.Select(reference => (reference, RhsOfLetAssignment(reference)))
103+
.Where(tpl => tpl.Item2 != null);
104+
}
105+
106+
private static ParserRuleContext RhsOfLetAssignment(IdentifierReference letAssignment)
107+
{
108+
var letStatement = letAssignment.Context.Parent as VBAParser.LetStmtContext;
109+
return letStatement?.expression();
110+
}
111+
112+
private static IEnumerable<IdentifierReference> PossiblyObjectNonSetAssignments(QualifiedModuleName module, DeclarationFinder finder)
113+
{
114+
var assignments = finder.IdentifierReferences(module)
115+
.Where(reference => reference.IsAssignment
116+
&& !reference.IsSetAssignment
117+
&& (reference.IsNonIndexedDefaultMemberAccess
118+
|| Tokens.Variant.Equals(reference.Declaration.AsTypeName, StringComparison.InvariantCultureIgnoreCase)));
119+
var unboundAssignments = finder.UnboundDefaultMemberAccesses(module)
120+
.Where(reference => reference.IsAssignment);
121+
122+
return assignments.Concat(unboundAssignments);
69123
}
70124
}
71125
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Rubberduck.Inspections.Abstract;
4+
using Rubberduck.Inspections.Inspections.Extensions;
5+
using Rubberduck.Inspections.Results;
6+
using Rubberduck.Parsing.Inspections;
7+
using Rubberduck.Parsing.Inspections.Abstract;
8+
using Rubberduck.Parsing.Symbols;
9+
using Rubberduck.Parsing.VBA;
10+
using Rubberduck.Resources.Inspections;
11+
12+
namespace Rubberduck.CodeAnalysis.Inspections.Concrete
13+
{
14+
/// <summary>
15+
/// Locates places in which a procedure needs to be called but an object variables has been provided that does not have a suitable default member.
16+
/// </summary>
17+
/// <why>
18+
/// The VBA compiler does not check whether the necessary default member is present. Instead there is a runtime error whenever the runtime type fails to have the default member.
19+
/// </why>
20+
/// <example hasResult="true">
21+
/// <![CDATA[
22+
/// Class1:
23+
///
24+
/// Public Sub Foo()
25+
/// 'No default member attribute
26+
/// End Sub
27+
///
28+
/// ------------------------------
29+
/// Module1:
30+
///
31+
/// Public Sub DoIt()
32+
/// Dim cls As Class1
33+
/// Set cls = New Class1
34+
/// cls
35+
/// End Sub
36+
/// ]]>
37+
/// </example>
38+
/// <example hasResult="false">
39+
/// <![CDATA[
40+
/// Class1:
41+
///
42+
/// Public Sub Foo()
43+
/// Attribute Foo.UserMemId = 0
44+
/// End Sub
45+
///
46+
/// ------------------------------
47+
/// Module1:
48+
///
49+
/// Public Sub DoIt()
50+
/// Dim cls As Class1
51+
/// Set cls = New Class1
52+
/// cls
53+
/// End Sub
54+
/// ]]>
55+
/// </example>
56+
public class ProcedureRequiredInspection : InspectionBase
57+
{
58+
private readonly IDeclarationFinderProvider _declarationFinderProvider;
59+
60+
public ProcedureRequiredInspection(RubberduckParserState state)
61+
: base(state)
62+
{
63+
_declarationFinderProvider = state;
64+
65+
//This will most likely cause a runtime error. The exceptions are rare and should be refactored or made explicit with an @Ignore annotation.
66+
Severity = CodeInspectionSeverity.Error;
67+
}
68+
69+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
70+
{
71+
var finder = _declarationFinderProvider.DeclarationFinder;
72+
73+
var failedProcedureCoercions = finder.FailedProcedureCoercions();
74+
return failedProcedureCoercions
75+
.Where(failedCoercion => !IsIgnored(failedCoercion))
76+
.Select(failedCoercion => InspectionResult(failedCoercion, _declarationFinderProvider));
77+
}
78+
79+
private bool IsIgnored(IdentifierReference assignment)
80+
{
81+
return assignment.IsIgnoringInspectionResultFor(AnnotationName);
82+
}
83+
84+
private IInspectionResult InspectionResult(IdentifierReference failedCoercion, IDeclarationFinderProvider declarationFinderProvider)
85+
{
86+
return new IdentifierReferenceInspectionResult(this,
87+
ResultDescription(failedCoercion),
88+
declarationFinderProvider,
89+
failedCoercion);
90+
}
91+
92+
private string ResultDescription(IdentifierReference failedCoercion)
93+
{
94+
var expression = failedCoercion.IdentifierName;
95+
var typeName = failedCoercion.Declaration?.FullAsTypeName;
96+
return string.Format(InspectionResults.ProcedureRequiredInspection, expression, typeName);
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)