Skip to content

Commit bade5bc

Browse files
committed
Add a mechanism to ask an inspection result whether it gets invalidated
This commit introduces the method ChangesInvalidateResult on the IInspectionResult that returns whether a change of the specified modules makes the result invalid. The base implementation returns true if the module specified on the result gets invalidated or else forwards the request to the inspection to allow inspection specific handling. Declaration- and IdentifierReferenceInspectionResults also get invalidated based on the state they carry. AggregateInspectionResults are always considered invalidated as there is no way to see the individual results.
1 parent 99c2d75 commit bade5bc

File tree

9 files changed

+249
-12
lines changed

9 files changed

+249
-12
lines changed

Rubberduck.CodeAnalysis/Inspections/Abstract/InspectionBase.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,10 @@ public IEnumerable<IInspectionResult> GetInspectionResults(CancellationToken tok
166166
_logger.Trace("Intercepted invocation of '{0}.{1}' ran for {2}ms", GetType().Name, nameof(DoGetInspectionResults), _stopwatch.ElapsedMilliseconds);
167167
return result;
168168
}
169+
170+
public virtual bool ChangesInvalidateResult(IInspectionResult result, ICollection<QualifiedModuleName> modifiedModules)
171+
{
172+
return true;
173+
}
169174
}
170175
}

Rubberduck.CodeAnalysis/Inspections/Abstract/InspectionResultBase.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.IO;
1+
using System.Collections.Generic;
2+
using System.IO;
23
using Antlr4.Runtime;
34
using Rubberduck.Common;
45
using Rubberduck.Parsing.Inspections;
@@ -39,6 +40,12 @@ protected InspectionResultBase(IInspection inspection,
3940
public Declaration Target { get; }
4041
public dynamic Properties { get; }
4142

43+
public virtual bool ChangesInvalidateResult(ICollection<QualifiedModuleName> modifiedModules)
44+
{
45+
return modifiedModules.Contains(QualifiedName)
46+
|| Inspection.ChangesInvalidateResult(this, modifiedModules);
47+
}
48+
4249
/// <summary>
4350
/// Gets the information needed to select the target instruction in the VBE.
4451
/// </summary>

Rubberduck.CodeAnalysis/Inspections/Results/DeclarationInspectionResult.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
using Rubberduck.Inspections.Abstract;
1+
using System.Collections.Generic;
2+
using Rubberduck.Inspections.Abstract;
23
using Rubberduck.Parsing;
34
using Rubberduck.Parsing.Inspections.Abstract;
45
using Rubberduck.Parsing.Symbols;
56
using Rubberduck.VBEditor;
67

78
namespace Rubberduck.Inspections.Results
89
{
9-
internal class DeclarationInspectionResult : InspectionResultBase
10+
public class DeclarationInspectionResult : InspectionResultBase
1011
{
1112
public DeclarationInspectionResult(IInspection inspection, string description, Declaration target, QualifiedContext context = null, dynamic properties = null) :
1213
base(inspection,
@@ -31,5 +32,11 @@ public DeclarationInspectionResult(IInspection inspection, string description, D
3132
? target.QualifiedName
3233
: GetQualifiedMemberName(target.ParentDeclaration);
3334
}
35+
36+
public override bool ChangesInvalidateResult(ICollection<QualifiedModuleName> modifiedModules)
37+
{
38+
return modifiedModules.Contains(Target.QualifiedModuleName)
39+
|| base.ChangesInvalidateResult(modifiedModules);
40+
}
3441
}
3542
}
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Linq;
1+
using System.Collections.Generic;
2+
using System.Linq;
23
using Rubberduck.Inspections.Abstract;
34
using Rubberduck.Parsing;
45
using Rubberduck.Parsing.Inspections.Abstract;
@@ -8,24 +9,30 @@
89

910
namespace Rubberduck.Inspections.Results
1011
{
11-
internal class IdentifierReferenceInspectionResult : InspectionResultBase
12+
public class IdentifierReferenceInspectionResult : InspectionResultBase
1213
{
13-
public IdentifierReferenceInspectionResult(IInspection inspection, string description, RubberduckParserState state, IdentifierReference reference, dynamic properties = null) :
14+
public IdentifierReferenceInspectionResult(IInspection inspection, string description, IDeclarationFinderProvider declarationFinderProvider, IdentifierReference reference, dynamic properties = null) :
1415
base(inspection,
1516
description,
1617
reference.QualifiedModuleName,
1718
reference.Context,
1819
reference.Declaration,
1920
new QualifiedSelection(reference.QualifiedModuleName, reference.Context.GetSelection()),
20-
GetQualifiedMemberName(state, reference),
21+
GetQualifiedMemberName(declarationFinderProvider, reference),
2122
(object)properties)
2223
{
2324
}
2425

25-
private static QualifiedMemberName? GetQualifiedMemberName(RubberduckParserState state, IdentifierReference reference)
26+
private static QualifiedMemberName? GetQualifiedMemberName(IDeclarationFinderProvider declarationFinderProvider, IdentifierReference reference)
2627
{
27-
var members = state.DeclarationFinder.Members(reference.QualifiedModuleName);
28+
var members = declarationFinderProvider.DeclarationFinder.Members(reference.QualifiedModuleName);
2829
return members.SingleOrDefault(m => reference.Context.IsDescendentOf(m.Context))?.QualifiedName;
2930
}
31+
32+
public override bool ChangesInvalidateResult(ICollection<QualifiedModuleName> modifiedModules)
33+
{
34+
return modifiedModules.Contains(Target.QualifiedModuleName)
35+
|| base.ChangesInvalidateResult(modifiedModules);
36+
}
3037
}
3138
}

Rubberduck.CodeAnalysis/Inspections/Results/QualifiedContextInspectionResult.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace Rubberduck.Inspections.Results
77
{
8-
internal class QualifiedContextInspectionResult : InspectionResultBase
8+
public class QualifiedContextInspectionResult : InspectionResultBase
99
{
1010
public QualifiedContextInspectionResult(IInspection inspection, string description, QualifiedContext context, dynamic properties = null) :
1111
base(inspection,
@@ -16,7 +16,6 @@ public QualifiedContextInspectionResult(IInspection inspection, string descripti
1616
new QualifiedSelection(context.ModuleName, context.Context.GetSelection()),
1717
context.MemberName,
1818
(object)properties)
19-
{
20-
}
19+
{}
2120
}
2221
}

Rubberduck.Core/UI/Inspections/AggregateInspectionResult.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using Antlr4.Runtime;
34
using Rubberduck.Parsing.Inspections.Abstract;
45
using Rubberduck.Parsing.Symbols;
@@ -29,6 +30,8 @@ public AggregateInspectionResult(IInspectionResult firstResult, int count)
2930

3031
public dynamic Properties => throw new InvalidOperationException();
3132

33+
public virtual bool ChangesInvalidateResult(ICollection<QualifiedModuleName> modifiedModules) => true;
34+
3235
public int CompareTo(IInspectionResult other)
3336
{
3437
if (other == this)

Rubberduck.Parsing/Inspections/Abstract/IInspection.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Threading;
4+
using Rubberduck.VBEditor;
45

56
namespace Rubberduck.Parsing.Inspections.Abstract
67
{
@@ -15,5 +16,10 @@ public interface IInspection : IInspectionModel, IComparable<IInspection>, IComp
1516
/// <param name="token"></param>
1617
/// <returns>Returns inspection results, if any.</returns>
1718
IEnumerable<IInspectionResult> GetInspectionResults(CancellationToken token);
19+
20+
/// <summary>
21+
/// Specifies whether an inspection result is deemed invalid after the specified modules have changed.
22+
/// </summary>
23+
bool ChangesInvalidateResult(IInspectionResult result, ICollection<QualifiedModuleName> modifiedModules);
1824
}
1925
}

Rubberduck.Parsing/Inspections/Abstract/IInspectionResult.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using Antlr4.Runtime;
34
using Rubberduck.Parsing.Symbols;
45
using Rubberduck.VBEditor;
@@ -14,5 +15,6 @@ public interface IInspectionResult : IComparable<IInspectionResult>, IComparable
1415
Declaration Target { get; }
1516
ParserRuleContext Context { get; }
1617
dynamic Properties { get; }
18+
bool ChangesInvalidateResult(ICollection<QualifiedModuleName> modifiedModules);
1719
}
1820
}
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
using System.Collections.Generic;
2+
using Moq;
3+
using NUnit.Framework;
4+
using Rubberduck.Inspections.Results;
5+
using Rubberduck.Parsing;
6+
using Rubberduck.Parsing.Annotations;
7+
using Rubberduck.Parsing.Inspections.Abstract;
8+
using Rubberduck.Parsing.Symbols;
9+
using Rubberduck.Parsing.VBA;
10+
using Rubberduck.Parsing.VBA.DeclarationCaching;
11+
using Rubberduck.UI.Inspections;
12+
using Rubberduck.VBEditor;
13+
14+
namespace RubberduckTests.Inspections
15+
{
16+
[TestFixture]
17+
public class InspectionResultTests
18+
{
19+
[Test]
20+
public void InspectionResultsAreDeemedInvalidatedIfTheModuleWithTheirQualifiedModuleNameHasBeenModified()
21+
{
22+
var inspectionMock = new Mock<IInspection>();
23+
inspectionMock
24+
.Setup(m =>
25+
m.ChangesInvalidateResult(It.IsAny<IInspectionResult>(),
26+
It.IsAny<ICollection<QualifiedModuleName>>()))
27+
.Returns(false);
28+
29+
var module = new QualifiedModuleName("project", string.Empty,"module");
30+
var context = new QualifiedContext(module, null);
31+
var modifiedModules = new HashSet<QualifiedModuleName>{module};
32+
33+
var inspectionResult = new QualifiedContextInspectionResult(inspectionMock.Object, string.Empty, context);
34+
35+
Assert.IsTrue(inspectionResult.ChangesInvalidateResult(modifiedModules));
36+
}
37+
38+
[Test]
39+
public void InspectionResultsAreDeemedInvalidatedIfTheInspectionDeemsThemInvalidated()
40+
{
41+
var inspectionMock = new Mock<IInspection>();
42+
inspectionMock
43+
.Setup(m =>
44+
m.ChangesInvalidateResult(It.IsAny<IInspectionResult>(),
45+
It.IsAny<ICollection<QualifiedModuleName>>()))
46+
.Returns(true);
47+
48+
var module = new QualifiedModuleName("project", string.Empty, "module");
49+
var context = new QualifiedContext(module, null);
50+
var modifiedModules = new HashSet<QualifiedModuleName>();
51+
52+
var inspectionResult = new QualifiedContextInspectionResult(inspectionMock.Object, string.Empty, context);
53+
54+
Assert.IsTrue(inspectionResult.ChangesInvalidateResult(modifiedModules));
55+
}
56+
57+
[Test]
58+
public void QualifiedContextInspectionResultsAreNotDeemedInvalidatedIfNeitherTheInspectionDeemsThemInvalidatedNorTheirQualifiedModuleNameGetsModified()
59+
{
60+
var inspectionMock = new Mock<IInspection>();
61+
inspectionMock
62+
.Setup(m =>
63+
m.ChangesInvalidateResult(It.IsAny<IInspectionResult>(),
64+
It.IsAny<ICollection<QualifiedModuleName>>()))
65+
.Returns(false);
66+
67+
var module = new QualifiedModuleName("project", string.Empty, "module");
68+
var otherModule = new QualifiedModuleName("project", string.Empty, "otherModule");
69+
var context = new QualifiedContext(module, null);
70+
var modifiedModules = new HashSet<QualifiedModuleName>{ otherModule };
71+
72+
var inspectionResult = new QualifiedContextInspectionResult(inspectionMock.Object, string.Empty, context);
73+
74+
Assert.IsFalse(inspectionResult.ChangesInvalidateResult(modifiedModules));
75+
}
76+
77+
[Test]
78+
public void DeclarationInspectionResultsAreDeemedInvalidatedIfTheirTargetsModuleGetsModified()
79+
{
80+
var inspectionMock = new Mock<IInspection>();
81+
inspectionMock
82+
.Setup(m =>
83+
m.ChangesInvalidateResult(It.IsAny<IInspectionResult>(),
84+
It.IsAny<ICollection<QualifiedModuleName>>()))
85+
.Returns(false);
86+
87+
var module = new QualifiedModuleName("project", string.Empty, "module");
88+
var declarationModule = new QualifiedModuleName("project", string.Empty, "declarationModule");
89+
var declarationMemberName = new QualifiedMemberName(declarationModule, "test");
90+
var context = new QualifiedContext(module, null);
91+
var declaration = new Declaration(declarationMemberName, null, string.Empty, string.Empty, string.Empty, false, false,
92+
Accessibility.Public, DeclarationType.Constant, null, null, default, false, null);
93+
var modifiedModules = new HashSet<QualifiedModuleName>{declarationModule};
94+
95+
var inspectionResult = new DeclarationInspectionResult(inspectionMock.Object, string.Empty, declaration, context);
96+
97+
Assert.IsTrue(inspectionResult.ChangesInvalidateResult(modifiedModules));
98+
}
99+
100+
[Test]
101+
public void DeclarationInspectionResultsAreNotDeemedInvalidatedIfNeitherTheInspectionDeemsThemInvalidatedNorTheirModuleNorThatOfTheTargetGetModified()
102+
{
103+
var inspectionMock = new Mock<IInspection>();
104+
inspectionMock
105+
.Setup(m =>
106+
m.ChangesInvalidateResult(It.IsAny<IInspectionResult>(),
107+
It.IsAny<ICollection<QualifiedModuleName>>()))
108+
.Returns(false);
109+
110+
var module = new QualifiedModuleName("project", string.Empty, "module");
111+
var declarationModule = new QualifiedModuleName("project", string.Empty, "declarationModule");
112+
var otherModule = new QualifiedModuleName("project", string.Empty, "otherModule");
113+
var declarationMemberName = new QualifiedMemberName(declarationModule, "test");
114+
var context = new QualifiedContext(module, null);
115+
var declaration = new Declaration(declarationMemberName, null, string.Empty, string.Empty, string.Empty, false, false,
116+
Accessibility.Public, DeclarationType.Constant, null, null, default, false, null);
117+
var modifiedModules = new HashSet<QualifiedModuleName> { otherModule };
118+
119+
var inspectionResult = new DeclarationInspectionResult(inspectionMock.Object, string.Empty, declaration, context);
120+
121+
Assert.IsFalse(inspectionResult.ChangesInvalidateResult(modifiedModules));
122+
}
123+
124+
[Test]
125+
public void IdentifierRefereneceInspectionResultsAreDeemedInvalidatedIfTheModuleOfTheirReferencedDeclarationGetsModified()
126+
{
127+
var inspectionMock = new Mock<IInspection>();
128+
inspectionMock
129+
.Setup(m =>
130+
m.ChangesInvalidateResult(It.IsAny<IInspectionResult>(),
131+
It.IsAny<ICollection<QualifiedModuleName>>()))
132+
.Returns(false);
133+
134+
var module = new QualifiedModuleName("project", string.Empty, "module");
135+
var declarationModule = new QualifiedModuleName("project", string.Empty, "declarationModule");
136+
var declarationMemberName = new QualifiedMemberName(declarationModule, "test");
137+
var declaration = new Declaration(declarationMemberName, null, string.Empty, string.Empty, string.Empty, false, false,
138+
Accessibility.Public, DeclarationType.Constant, null, null, default, false, null);
139+
var identifierReference = new IdentifierReference(module, null, null, "test", default, null, declaration);
140+
var modifiedModules = new HashSet<QualifiedModuleName> { declarationModule };
141+
142+
var declarationFinderProviderMock = new Mock<IDeclarationFinderProvider>();
143+
var declaratioFinder = new DeclarationFinder(new List<Declaration>(), new List<IAnnotation>(),
144+
new List<UnboundMemberDeclaration>());
145+
declarationFinderProviderMock.SetupGet(m => m.DeclarationFinder).Returns(declaratioFinder);
146+
var inspectionResult = new IdentifierReferenceInspectionResult(inspectionMock.Object, string.Empty, declarationFinderProviderMock.Object, identifierReference);
147+
148+
Assert.IsTrue(inspectionResult.ChangesInvalidateResult(modifiedModules));
149+
}
150+
151+
[Test]
152+
public void IdentifierReferenceInspectionResultsAreNotDeemedInvalidatedIfNeitherTheInspectionDeemsThemInvalidatedNorTheirModuleNorThatOfTheReferencedDeclarationGetModified()
153+
{
154+
var inspectionMock = new Mock<IInspection>();
155+
inspectionMock
156+
.Setup(m =>
157+
m.ChangesInvalidateResult(It.IsAny<IInspectionResult>(),
158+
It.IsAny<ICollection<QualifiedModuleName>>()))
159+
.Returns(false);
160+
161+
var module = new QualifiedModuleName("project", string.Empty, "module");
162+
var declarationModule = new QualifiedModuleName("project", string.Empty, "declarationModule");
163+
var otherModule = new QualifiedModuleName("project", string.Empty, "otherModule");
164+
var declarationMemberName = new QualifiedMemberName(declarationModule, "test");
165+
var declaration = new Declaration(declarationMemberName, null, string.Empty, string.Empty, string.Empty, false, false,
166+
Accessibility.Public, DeclarationType.Constant, null, null, default, false, null);
167+
168+
var identifierReference = new IdentifierReference(module, null, null, "test", default, null, declaration);
169+
var modifiedModules = new HashSet<QualifiedModuleName> { otherModule };
170+
171+
var declarationFinderProviderMock = new Mock<IDeclarationFinderProvider>();
172+
var declaratioFinder = new DeclarationFinder(new List<Declaration>(), new List<IAnnotation>(),
173+
new List<UnboundMemberDeclaration>());
174+
declarationFinderProviderMock.SetupGet(m => m.DeclarationFinder).Returns(declaratioFinder);
175+
var inspectionResult = new IdentifierReferenceInspectionResult(inspectionMock.Object, string.Empty, declarationFinderProviderMock.Object, identifierReference);
176+
177+
Assert.IsFalse(inspectionResult.ChangesInvalidateResult(modifiedModules));
178+
}
179+
180+
[Test]
181+
public void AggregateInspectionResultsAreAlwaysDeemedInvalidated()
182+
{
183+
var inspectionMock = new Mock<IInspection>();
184+
inspectionMock
185+
.Setup(m =>
186+
m.ChangesInvalidateResult(It.IsAny<IInspectionResult>(),
187+
It.IsAny<ICollection<QualifiedModuleName>>()))
188+
.Returns(false);
189+
190+
var module = new QualifiedModuleName("project", string.Empty, "module");
191+
var otherModule = new QualifiedModuleName("project", string.Empty, "otherModule");
192+
var context = new QualifiedContext(module, null);
193+
var modifiedModules = new HashSet<QualifiedModuleName> { otherModule };
194+
195+
var baseInspectionResult = new QualifiedContextInspectionResult(inspectionMock.Object, string.Empty, context);
196+
var inspectionResult = new AggregateInspectionResult(baseInspectionResult, 42);
197+
198+
Assert.IsTrue(inspectionResult.ChangesInvalidateResult(modifiedModules));
199+
}
200+
}
201+
}

0 commit comments

Comments
 (0)