Skip to content

Commit 4a7b243

Browse files
committed
Merge changes to next into feature-branch
2 parents 5a5e070 + 71b9554 commit 4a7b243

File tree

45 files changed

+934
-179
lines changed

Some content is hidden

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

45 files changed

+934
-179
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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.Grammar;
7+
using Rubberduck.Parsing.Inspections;
8+
using Rubberduck.Parsing.Inspections.Abstract;
9+
using Rubberduck.Parsing.Symbols;
10+
using Rubberduck.Parsing.TypeResolvers;
11+
using Rubberduck.Parsing.VBA;
12+
using Rubberduck.Parsing.VBA.DeclarationCaching;
13+
using Rubberduck.Resources.Inspections;
14+
using Rubberduck.VBEditor;
15+
16+
namespace Rubberduck.CodeAnalysis.Inspections.Concrete
17+
{
18+
/// <summary>
19+
/// Locates arguments passed to functions or procedures for object parameters which the do not have a compatible declared type.
20+
/// </summary>
21+
/// <why>
22+
/// The VBA compiler does not check whether different object types are compatible. Instead there is a runtime error whenever the types are incompatible.
23+
/// </why>
24+
/// <example hasResult="true">
25+
/// <![CDATA[
26+
/// IInterface:
27+
///
28+
/// Public Sub DoSomething()
29+
/// End Sub
30+
///
31+
/// ------------------------------
32+
/// Class1:
33+
///
34+
///'No Implements IInterface
35+
///
36+
/// Public Sub DoSomething()
37+
/// End Sub
38+
///
39+
/// ------------------------------
40+
/// Module1:
41+
///
42+
/// Public Sub DoIt()
43+
/// Dim cls As Class1
44+
/// Set cls = New Class1
45+
/// Foo cls
46+
/// End Sub
47+
///
48+
/// Public Sub Foo(cls As IInterface)
49+
/// End Sub
50+
/// ]]>
51+
/// </example>
52+
/// <example hasResult="false">
53+
/// <![CDATA[
54+
/// IInterface:
55+
///
56+
/// Public Sub DoSomething()
57+
/// End Sub
58+
///
59+
/// ------------------------------
60+
/// Class1:
61+
///
62+
/// Implements IInterface
63+
///
64+
/// Private Sub IInterface_DoSomething()
65+
/// End Sub
66+
///
67+
/// ------------------------------
68+
/// Module1:
69+
///
70+
/// Public Sub DoIt()
71+
/// Dim cls As Class1
72+
/// Set cls = New Class1
73+
/// Foo cls
74+
/// End Sub
75+
///
76+
/// Public Sub Foo(cls As IInterface)
77+
/// End Sub
78+
/// ]]>
79+
/// </example>
80+
public class ArgumentWithIncompatibleObjectTypeInspection : InspectionBase
81+
{
82+
private readonly IDeclarationFinderProvider _declarationFinderProvider;
83+
private readonly ISetTypeResolver _setTypeResolver;
84+
85+
public ArgumentWithIncompatibleObjectTypeInspection(RubberduckParserState state, ISetTypeResolver setTypeResolver)
86+
: base(state)
87+
{
88+
_declarationFinderProvider = state;
89+
_setTypeResolver = setTypeResolver;
90+
91+
//This will most likely cause a runtime error. The exceptions are rare and should be refactored or made explicit with an @Ignore annotation.
92+
Severity = CodeInspectionSeverity.Error;
93+
}
94+
95+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
96+
{
97+
var finder = _declarationFinderProvider.DeclarationFinder;
98+
99+
var strictlyTypedObjectParameters = finder.DeclarationsWithType(DeclarationType.Parameter)
100+
.Where(ToBeConsidered)
101+
.OfType<ParameterDeclaration>();
102+
103+
var offendingArguments = strictlyTypedObjectParameters
104+
.SelectMany(param => param.ArgumentReferences)
105+
.Select(argumentReference => ArgumentReferenceWithArgumentTypeName(argumentReference, finder))
106+
.Where(argumentReferenceWithTypeName => argumentReferenceWithTypeName.argumentTypeName != null
107+
&& !ArgumentPossiblyLegal(
108+
argumentReferenceWithTypeName.argumentReference.Declaration,
109+
argumentReferenceWithTypeName.argumentTypeName));
110+
111+
return offendingArguments
112+
.Where(argumentReferenceWithTypeName => !IsIgnored(argumentReferenceWithTypeName.Item1))
113+
.Select(argumentReference => InspectionResult(argumentReference, _declarationFinderProvider));
114+
}
115+
116+
private static bool ToBeConsidered(Declaration declaration)
117+
{
118+
return declaration != null
119+
&& declaration.AsTypeDeclaration != null
120+
&& declaration.IsObject;
121+
}
122+
123+
private (IdentifierReference argumentReference, string argumentTypeName) ArgumentReferenceWithArgumentTypeName(IdentifierReference argumentReference, DeclarationFinder finder)
124+
{
125+
return (argumentReference, ArgumentSetTypeName(argumentReference, finder));
126+
}
127+
128+
private string ArgumentSetTypeName(IdentifierReference argumentReference, DeclarationFinder finder)
129+
{
130+
var argumentExpression = argumentReference.Context as VBAParser.ExpressionContext;
131+
return SetTypeNameOfExpression(argumentExpression, argumentReference.QualifiedModuleName, finder);
132+
}
133+
134+
private string SetTypeNameOfExpression(VBAParser.ExpressionContext expression, QualifiedModuleName containingModule, DeclarationFinder finder)
135+
{
136+
return _setTypeResolver.SetTypeName(expression, containingModule);
137+
}
138+
139+
private bool ArgumentPossiblyLegal(Declaration parameterDeclaration , string assignedTypeName)
140+
{
141+
return assignedTypeName == parameterDeclaration.FullAsTypeName
142+
|| assignedTypeName == Tokens.Variant
143+
|| assignedTypeName == Tokens.Object
144+
|| HasBaseType(parameterDeclaration, assignedTypeName)
145+
|| HasSubType(parameterDeclaration, assignedTypeName);
146+
}
147+
148+
private bool HasBaseType(Declaration declaration, string typeName)
149+
{
150+
var ownType = declaration.AsTypeDeclaration;
151+
if (ownType == null || !(ownType is ClassModuleDeclaration classType))
152+
{
153+
return false;
154+
}
155+
156+
return classType.Subtypes.Select(subtype => subtype.QualifiedModuleName.ToString()).Contains(typeName);
157+
}
158+
159+
private bool HasSubType(Declaration declaration, string typeName)
160+
{
161+
var ownType = declaration.AsTypeDeclaration;
162+
if (ownType == null || !(ownType is ClassModuleDeclaration classType))
163+
{
164+
return false;
165+
}
166+
167+
return classType.Supertypes.Select(supertype => supertype.QualifiedModuleName.ToString()).Contains(typeName);
168+
}
169+
170+
private bool IsIgnored(IdentifierReference assignment)
171+
{
172+
return assignment.IsIgnoringInspectionResultFor(AnnotationName)
173+
// Ignoring the Declaration disqualifies all assignments
174+
|| assignment.Declaration.IsIgnoringInspectionResultFor(AnnotationName);
175+
}
176+
177+
private IInspectionResult InspectionResult((IdentifierReference argumentReference, string argumentTypeName) argumentReferenceWithTypeName, IDeclarationFinderProvider declarationFinderProvider)
178+
{
179+
var (argumentReference, argumentTypeName) = argumentReferenceWithTypeName;
180+
return new IdentifierReferenceInspectionResult(this,
181+
ResultDescription(argumentReference, argumentTypeName),
182+
declarationFinderProvider,
183+
argumentReference);
184+
}
185+
186+
private string ResultDescription(IdentifierReference argumentReference, string argumentTypeName)
187+
{
188+
var parameterName = argumentReference.Declaration.IdentifierName;
189+
var parameterTypeName = argumentReference.Declaration.FullAsTypeName;
190+
var argumentExpression = argumentReference.Context.GetText();
191+
return string.Format(InspectionResults.SetAssignmentWithIncompatibleObjectTypeInspection, parameterName, parameterTypeName, argumentExpression, argumentTypeName);
192+
}
193+
}
194+
}

Rubberduck.CodeAnalysis/Inspections/Concrete/ImplementedInterfaceMemberInspection.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,28 @@
1111
namespace Rubberduck.Inspections.Concrete
1212
{
1313
/// <summary>
14-
/// Identifies implemented members of class modules that are used as interfaces.
14+
/// Identifies members of class modules that are used as interfaces, but that have a concrete implementation.
1515
/// </summary>
1616
/// <why>
17-
/// Interfaces provide a unified programmatic access to different objects, and therefore are rarely instantiated as concrete objects.
17+
/// Interfaces provide an abstract, unified programmatic access to different objects; concrete implementations of their members should be in a separate module that 'Implements' the interface.
1818
/// </why>
1919
/// <example hasResults="false">
2020
/// <![CDATA[
21-
/// Sub Foo()
22-
/// ' ...
21+
/// Option Explicit
22+
/// '@Interface
23+
///
24+
/// Public Sub DoSomething()
25+
/// ' empty interface stub
2326
/// End Sub
2427
/// ]]>
2528
/// </example>
2629
/// <example hasResults="true">
2730
/// <![CDATA[
28-
/// Sub Foo()
29-
/// MsgBox "?"
31+
/// Option Explicit
32+
/// '@Interface
33+
///
34+
/// Public Sub DoSomething()
35+
/// MsgBox "Hello from interface!"
3036
/// End Sub
3137
/// ]]>
3238
/// </example>
@@ -43,11 +49,12 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
4349
&& !member.IsIgnoringInspectionResultFor(AnnotationName)))
4450
.Select(result => new DeclarationInspectionResult(this,
4551
string.Format(InspectionResults.ImplementedInterfaceMemberInspection,
52+
result.QualifiedModuleName.ToString(),
4653
Resources.RubberduckUI.ResourceManager
4754
.GetString("DeclarationType_" + result.DeclarationType)
4855
.Capitalize(),
4956
result.IdentifierName),
5057
result));
5158
}
5259
}
53-
}
60+
}

Rubberduck.CodeAnalysis/Inspections/Concrete/SetAssignmentWithIncompatibleObjectTypeInspection.cs

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -16,71 +16,71 @@
1616

1717
namespace Rubberduck.CodeAnalysis.Inspections.Concrete
1818
{
19+
/// <summary>
20+
/// Locates assignments to object variables for which the RHS does not have a compatible declared type.
21+
/// </summary>
22+
/// <why>
23+
/// The VBA compiler does not check whether different object types are compatible. Instead there is a runtime error whenever the types are incompatible.
24+
/// </why>
25+
/// <example hasResult="true">
26+
/// <![CDATA[
27+
/// IInterface:
28+
///
29+
/// Public Sub DoSomething()
30+
/// End Sub
31+
///
32+
/// ------------------------------
33+
/// Class1:
34+
///
35+
///'No Implements IInterface
36+
///
37+
/// Public Sub DoSomething()
38+
/// End Sub
39+
///
40+
/// ------------------------------
41+
/// Module1:
42+
///
43+
/// Public Sub DoIt()
44+
/// Dim cls As Class1
45+
/// Dim intrfc As IInterface
46+
///
47+
/// Set cls = New Class1
48+
/// Set intrfc = cls
49+
/// End Sub
50+
/// ]]>
51+
/// </example>
52+
/// <example hasResult="false">
53+
/// <![CDATA[
54+
/// IInterface:
55+
///
56+
/// Public Sub DoSomething()
57+
/// End Sub
58+
///
59+
/// ------------------------------
60+
/// Class1:
61+
///
62+
/// Implements IInterface
63+
///
64+
/// Private Sub IInterface_DoSomething()
65+
/// End Sub
66+
///
67+
/// ------------------------------
68+
/// Module1:
69+
///
70+
/// Public Sub DoIt()
71+
/// Dim cls As Class1
72+
/// Dim intrfc As IInterface
73+
///
74+
/// Set cls = New Class1
75+
/// Set intrfc = cls
76+
/// End Sub
77+
/// ]]>
78+
/// </example>
1979
public class SetAssignmentWithIncompatibleObjectTypeInspection : InspectionBase
2080
{
2181
private readonly IDeclarationFinderProvider _declarationFinderProvider;
2282
private readonly ISetTypeResolver _setTypeResolver;
2383

24-
/// <summary>
25-
/// Locates assignments to object variables for which the RHS does not have a compatible declared type.
26-
/// </summary>
27-
/// <why>
28-
/// The VBA compiler does not check whether different object types are compatible. Instead there is a runtime error whenever the types are incompatible.
29-
/// </why>
30-
/// <example hasResult="true">
31-
/// <![CDATA[
32-
/// IInterface:
33-
///
34-
/// Public Sub DoSomething()
35-
/// End Sub
36-
///
37-
/// ------------------------------
38-
/// Class1:
39-
///
40-
///'No Implements IInterface
41-
///
42-
/// Public Sub DoSomething()
43-
/// End Sub
44-
///
45-
/// ------------------------------
46-
/// Module1:
47-
///
48-
/// Public Sub DoIt()
49-
/// Dim cls As Class1
50-
/// Dim intrfc As IInterface
51-
///
52-
/// Set cls = New Class1
53-
/// Set intrfc = cls
54-
/// End Sub
55-
/// ]]>
56-
/// </example>
57-
/// <example hasResult="false">
58-
/// <![CDATA[
59-
/// IInterface:
60-
///
61-
/// Public Sub DoSomething()
62-
/// End Sub
63-
///
64-
/// ------------------------------
65-
/// Class1:
66-
///
67-
/// Implements IInterface
68-
///
69-
/// Private Sub IInterface_DoSomething()
70-
/// End Sub
71-
///
72-
/// ------------------------------
73-
/// Module1:
74-
///
75-
/// Public Sub DoIt()
76-
/// Dim cls As Class1
77-
/// Dim intrfc As IInterface
78-
///
79-
/// Set cls = New Class1
80-
/// Set intrfc = cls
81-
/// End Sub
82-
/// ]]>
83-
/// </example>
8484
public SetAssignmentWithIncompatibleObjectTypeInspection(RubberduckParserState state, ISetTypeResolver setTypeResolver)
8585
: base(state)
8686
{
@@ -192,4 +192,4 @@ private string ResultDescription(IdentifierReference setAssignment, string assig
192192
return string.Format(InspectionResults.SetAssignmentWithIncompatibleObjectTypeInspection, declarationName, variableTypeName, assignedTypeName);
193193
}
194194
}
195-
}
195+
}

0 commit comments

Comments
 (0)