|
1 |
| -using System.Collections.Generic; |
2 | 1 | using System.Linq;
|
3 | 2 | using Rubberduck.Inspections.Abstract;
|
4 |
| -using Rubberduck.Inspections.Results; |
5 | 3 | using Rubberduck.Parsing;
|
6 | 4 | using Rubberduck.Parsing.Grammar;
|
7 |
| -using Rubberduck.Parsing.Inspections.Abstract; |
8 | 5 | using Rubberduck.Resources.Inspections;
|
9 | 6 | using Rubberduck.Parsing.Symbols;
|
10 | 7 | using Rubberduck.Parsing.VBA;
|
11 |
| -using Rubberduck.Inspections.Inspections.Extensions; |
| 8 | +using Rubberduck.Parsing.VBA.DeclarationCaching; |
12 | 9 |
|
13 | 10 | namespace Rubberduck.Inspections.Concrete
|
14 | 11 | {
|
@@ -37,37 +34,61 @@ namespace Rubberduck.Inspections.Concrete
|
37 | 34 | /// End Sub
|
38 | 35 | /// ]]>
|
39 | 36 | /// </example>
|
40 |
| - public sealed class VariableNotAssignedInspection : InspectionBase |
| 37 | + public sealed class VariableNotAssignedInspection : DeclarationInspectionBase |
41 | 38 | {
|
42 | 39 | public VariableNotAssignedInspection(RubberduckParserState state)
|
43 |
| - : base(state) { } |
| 40 | + : base(state, DeclarationType.Variable) { } |
44 | 41 |
|
45 |
| - protected override IEnumerable<IInspectionResult> DoGetInspectionResults() |
| 42 | + protected override bool IsResultDeclaration(Declaration declaration, DeclarationFinder finder) |
46 | 43 | {
|
47 |
| - // ignore arrays. todo: ArrayIndicesNotAccessedInspection |
48 |
| - var arrays = State.DeclarationFinder.UserDeclarations(DeclarationType.Variable).Where(declaration => declaration.IsArray); |
49 |
| - |
50 |
| - var declarations = State.DeclarationFinder.UserDeclarations(DeclarationType.Variable) |
51 |
| - .Except(arrays) |
52 |
| - .Where(declaration => |
53 |
| - !declaration.IsWithEvents |
54 |
| - && State.DeclarationFinder.MatchName(declaration.AsTypeName).All(item => item.DeclarationType != DeclarationType.UserDefinedType) // UDT variables don't need to be assigned |
55 |
| - && !declaration.IsSelfAssigned |
56 |
| - && !declaration.References.Any(reference => reference.IsAssignment || IsAssignedByRefArgument(reference.ParentScoping, reference))); |
| 44 | + return declaration != null |
| 45 | + && !declaration.IsArray // ignore arrays. todo: ArrayIndicesNotAccessedInspection |
| 46 | + && !declaration.IsWithEvents |
| 47 | + && !declaration.IsSelfAssigned |
| 48 | + && !HasUdtType(declaration, finder) // UDT variables don't need to be assigned |
| 49 | + && !declaration.References.Any(reference => reference.IsAssignment || IsAssignedByRefArgument(reference.ParentScoping, reference, finder)); |
| 50 | + } |
57 | 51 |
|
58 |
| - return declarations.Select(issue => |
59 |
| - new DeclarationInspectionResult(this, string.Format(InspectionResults.VariableNotAssignedInspection, issue.IdentifierName), issue)); |
| 52 | + private static bool HasUdtType(Declaration declaration, DeclarationFinder finder) |
| 53 | + { |
| 54 | + return finder.MatchName(declaration.AsTypeName) |
| 55 | + .Any(item => item.DeclarationType == DeclarationType.UserDefinedType); |
60 | 56 | }
|
61 | 57 |
|
62 |
| - private bool IsAssignedByRefArgument(Declaration enclosingProcedure, IdentifierReference reference) |
| 58 | + private static bool IsAssignedByRefArgument(Declaration enclosingProcedure, IdentifierReference reference, DeclarationFinder finder) |
63 | 59 | {
|
64 |
| - var argExpression = reference.Context.GetAncestor<VBAParser.ArgumentExpressionContext>(); |
65 |
| - var parameter = State.DeclarationFinder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, enclosingProcedure); |
| 60 | + var argExpression = ImmediateArgumentExpressionContext(reference); |
| 61 | + |
| 62 | + if (argExpression is null) |
| 63 | + { |
| 64 | + return false; |
| 65 | + } |
| 66 | + |
| 67 | + var parameter = finder.FindParameterOfNonDefaultMemberFromSimpleArgumentNotPassedByValExplicitly(argExpression, enclosingProcedure); |
66 | 68 |
|
67 | 69 | // note: not recursive, by design.
|
68 | 70 | return parameter != null
|
69 | 71 | && (parameter.IsImplicitByRef || parameter.IsByRef)
|
70 | 72 | && parameter.References.Any(r => r.IsAssignment);
|
71 | 73 | }
|
| 74 | + |
| 75 | + private static VBAParser.ArgumentExpressionContext ImmediateArgumentExpressionContext(IdentifierReference reference) |
| 76 | + { |
| 77 | + var context = reference.Context; |
| 78 | + //The context is either already a simpleNameExprContext or an IdentifierValueContext used in a sub-rule of some other lExpression alternative. |
| 79 | + var lExpressionNameContext = context is VBAParser.SimpleNameExprContext simpleName |
| 80 | + ? simpleName |
| 81 | + : context.GetAncestor<VBAParser.LExpressionContext>(); |
| 82 | + |
| 83 | + //To be an immediate argument and, thus, assignable by ref, the structure must be argumentExpression -> expression -> lExpression. |
| 84 | + return lExpressionNameContext? |
| 85 | + .Parent? |
| 86 | + .Parent as VBAParser.ArgumentExpressionContext; |
| 87 | + } |
| 88 | + |
| 89 | + protected override string ResultDescription(Declaration declaration) |
| 90 | + { |
| 91 | + return string.Format(InspectionResults.VariableNotAssignedInspection, declaration.IdentifierName); |
| 92 | + } |
72 | 93 | }
|
73 | 94 | }
|
0 commit comments