2
2
using System . Linq ;
3
3
using Rubberduck . Inspections . Abstract ;
4
4
using Rubberduck . Inspections . Results ;
5
+ using Rubberduck . Parsing ;
5
6
using Rubberduck . Parsing . Grammar ;
6
7
using Rubberduck . Parsing . Inspections . Abstract ;
7
8
using Rubberduck . Resources . Inspections ;
@@ -34,7 +35,8 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
34
35
var unassigned = ( from function in functions
35
36
let isUdt = IsReturningUserDefinedType ( function )
36
37
let inScopeRefs = function . References . Where ( r => r . ParentScoping . Equals ( function ) )
37
- where ( ! isUdt && ( ! inScopeRefs . Any ( r => r . IsAssignment ) ) )
38
+ where ( ! isUdt && ( ! inScopeRefs . Any ( r => r . IsAssignment ) &&
39
+ ! inScopeRefs . Any ( reference => IsAssignedByRefArgument ( function , reference ) ) ) )
38
40
|| ( isUdt && ! IsUserDefinedTypeAssigned ( function ) )
39
41
select function )
40
42
. ToList ( ) ;
@@ -46,6 +48,61 @@ protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
46
48
issue ) ) ;
47
49
}
48
50
51
+ private bool IsAssignedByRefArgument ( Declaration enclosingProcedure , IdentifierReference reference )
52
+ {
53
+ var argExpression = reference . Context . GetAncestor < VBAParser . ArgumentExpressionContext > ( ) ;
54
+ if ( argExpression ? . GetDescendent < VBAParser . ParenthesizedExprContext > ( ) != null || argExpression ? . BYVAL ( ) != null )
55
+ {
56
+ // not an argument, or argument is parenthesized and thus passed ByVal
57
+ return false ;
58
+ }
59
+
60
+ var callStmt = argExpression ? . GetAncestor < VBAParser . CallStmtContext > ( ) ;
61
+ var procedureName = callStmt ? . GetDescendent < VBAParser . LExpressionContext > ( )
62
+ . GetDescendents < VBAParser . IdentifierContext > ( )
63
+ . LastOrDefault ( ) ? . GetText ( ) ;
64
+ if ( procedureName == null )
65
+ {
66
+ // if we don't know what we're calling, we can't dig any further
67
+ return false ;
68
+ }
69
+
70
+ var procedure = State . DeclarationFinder . MatchName ( procedureName )
71
+ . Where ( p => AccessibilityCheck . IsAccessible ( enclosingProcedure , p ) )
72
+ . SingleOrDefault ( p => ! p . DeclarationType . HasFlag ( DeclarationType . Property ) || p . DeclarationType . HasFlag ( DeclarationType . PropertyGet ) ) ;
73
+ var parameters = State . DeclarationFinder . Parameters ( procedure ) ;
74
+
75
+ ParameterDeclaration parameter ;
76
+ var namedArg = argExpression . GetAncestor < VBAParser . NamedArgumentContext > ( ) ;
77
+ if ( namedArg != null )
78
+ {
79
+ // argument is named: we're lucky
80
+ var parameterName = namedArg . unrestrictedIdentifier ( ) . GetText ( ) ;
81
+ parameter = parameters . SingleOrDefault ( p => p . IdentifierName == parameterName ) ;
82
+ }
83
+ else
84
+ {
85
+ // argument is positional: work out its index
86
+ var argList = callStmt . GetDescendent < VBAParser . ArgumentListContext > ( ) ;
87
+ var args = argList . GetDescendents < VBAParser . PositionalArgumentContext > ( ) . ToArray ( ) ;
88
+ var parameterIndex = args . Select ( ( a , i ) =>
89
+ a . GetDescendent < VBAParser . ArgumentExpressionContext > ( ) == argExpression ? ( a , i ) : ( null , - 1 ) )
90
+ . SingleOrDefault ( item => item . a != null ) . i ;
91
+ parameter = parameters . OrderBy ( p => p . Selection ) . Select ( ( p , i ) => ( p , i ) )
92
+ . SingleOrDefault ( item => item . i == parameterIndex ) . p ;
93
+ }
94
+
95
+ if ( parameter == null )
96
+ {
97
+ // couldn't locate parameter
98
+ return false ;
99
+ }
100
+
101
+ // note: not recursive, by design.
102
+ return ( parameter . IsImplicitByRef || parameter . IsByRef )
103
+ && parameter . References . Any ( r => r . IsAssignment ) ;
104
+ }
105
+
49
106
private bool IsReturningUserDefinedType ( Declaration member )
50
107
{
51
108
return member . AsTypeDeclaration != null &&
0 commit comments