Skip to content

Commit 105cbdd

Browse files
committed
Get document supertypes from user com projects
We use the user com projects to get the names of the implemented interfaces; the type hierarchy pass does the rest. There is one hack in this: we suppress the underscore in front of the implemented interfaces in order to emulate the VBE's behaviour to let type checks pass relative to the coclass instead of the interface. This is entirely based on convention. Moreover, this lacks unit test since the com projects cannot be mocked at this stage.
1 parent 8695c05 commit 105cbdd

File tree

5 files changed

+66
-95
lines changed

5 files changed

+66
-95
lines changed

Rubberduck.Parsing/VBA/ReferenceManagement/CompilationPasses/TypeHierarchyPass.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ public TypeHierarchyPass(DeclarationFinder declarationFinder, VBAExpressionParse
3131

3232
public void Execute(IReadOnlyCollection<QualifiedModuleName> modules)
3333
{
34-
var toRelsolveSupertypesFor = _declarationFinder.UserDeclarations(DeclarationType.ClassModule)
34+
var toResolveSupertypesFor = _declarationFinder.UserDeclarations(DeclarationType.ClassModule)
3535
.Concat(_declarationFinder.UserDeclarations(DeclarationType.Document))
3636
.Concat(_declarationFinder.UserDeclarations(DeclarationType.UserForm))
3737
.Where(decl => modules.Contains(decl.QualifiedName.QualifiedModuleName))
3838
.Concat(_declarationFinder.BuiltInDeclarations(DeclarationType.ClassModule));
39-
foreach (var declaration in toRelsolveSupertypesFor)
39+
foreach (var declaration in toResolveSupertypesFor)
4040
{
4141
AddImplementedInterface(declaration);
4242
}

Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunner.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Threading;
66
using System.Threading.Tasks;
77
using Antlr4.Runtime.Tree;
8+
using Rubberduck.Parsing.ComReflection;
89
using Rubberduck.Parsing.VBA.DeclarationCaching;
910
using Rubberduck.VBEditor;
1011

@@ -17,13 +18,15 @@ public class ReferenceResolveRunner : ReferenceResolveRunnerBase
1718
public ReferenceResolveRunner(
1819
RubberduckParserState state,
1920
IParserStateManager parserStateManager,
20-
IModuleToModuleReferenceManager moduletToModuleReferenceManager,
21-
IReferenceRemover referenceRemover)
21+
IModuleToModuleReferenceManager moduleToModuleReferenceManager,
22+
IReferenceRemover referenceRemover,
23+
IUserComProjectProvider userComProjectProvider)
2224
:base(state,
2325
parserStateManager,
24-
moduletToModuleReferenceManager,
25-
referenceRemover)
26-
{ }
26+
moduleToModuleReferenceManager,
27+
referenceRemover,
28+
userComProjectProvider)
29+
{}
2730

2831

2932
protected override void ResolveReferences(ICollection<KeyValuePair<QualifiedModuleName, IParseTree>> toResolve, CancellationToken token)

Rubberduck.Parsing/VBA/ReferenceManagement/ReferenceResolveRunnerBase.cs

Lines changed: 46 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Antlr4.Runtime.Tree;
77
using NLog;
88
using Rubberduck.Parsing.Common;
9+
using Rubberduck.Parsing.ComReflection;
910
using Rubberduck.Parsing.Symbols;
1011
using Rubberduck.Parsing.VBA.DeclarationCaching;
1112
using Rubberduck.Parsing.VBA.Extensions;
@@ -29,12 +30,14 @@ public abstract class ReferenceResolveRunnerBase : IReferenceResolveRunner
2930
protected readonly IParserStateManager _parserStateManager;
3031
private readonly IModuleToModuleReferenceManager _moduleToModuleReferenceManager;
3132
private readonly IReferenceRemover _referenceRemover;
33+
private readonly IUserComProjectProvider _userComProjectProvider;
3234

3335
public ReferenceResolveRunnerBase(
3436
RubberduckParserState state,
3537
IParserStateManager parserStateManager,
36-
IModuleToModuleReferenceManager moduletToModuleReferenceManager,
37-
IReferenceRemover referenceRemover)
38+
IModuleToModuleReferenceManager moduleToModuleReferenceManager,
39+
IReferenceRemover referenceRemover,
40+
IUserComProjectProvider userComProjectProvider)
3841
{
3942
if (state == null)
4043
{
@@ -44,19 +47,24 @@ public ReferenceResolveRunnerBase(
4447
{
4548
throw new ArgumentNullException(nameof(parserStateManager));
4649
}
47-
if (moduletToModuleReferenceManager == null)
50+
if (moduleToModuleReferenceManager == null)
4851
{
49-
throw new ArgumentNullException(nameof(moduletToModuleReferenceManager));
52+
throw new ArgumentNullException(nameof(moduleToModuleReferenceManager));
5053
}
5154
if (referenceRemover == null)
5255
{
5356
throw new ArgumentNullException(nameof(referenceRemover));
5457
}
58+
if (userComProjectProvider == null)
59+
{
60+
throw new ArgumentNullException(nameof(userComProjectProvider));
61+
}
5562

5663
_state = state;
5764
_parserStateManager = parserStateManager;
58-
_moduleToModuleReferenceManager = moduletToModuleReferenceManager;
65+
_moduleToModuleReferenceManager = moduleToModuleReferenceManager;
5966
_referenceRemover = referenceRemover;
67+
_userComProjectProvider = userComProjectProvider;
6068
}
6169

6270

@@ -81,18 +89,18 @@ public void ResolveReferences(IReadOnlyCollection<QualifiedModuleName> toResolve
8189

8290
var parsingStageTimer = ParsingStageTimer.StartNew();
8391

84-
ExecuteCompilationPasses(_toResolve.AsReadOnly(), token);
92+
AddSuperTypeNamesForDocumentModules(_toResolve.AsReadOnly(), _state, _userComProjectProvider);
8593
token.ThrowIfCancellationRequested();
8694

8795
parsingStageTimer.Stop();
88-
parsingStageTimer.Log("Executed compilation passes in {0}ms.");
89-
parsingStageTimer.Restart();
96+
parsingStageTimer.Log("Added supertypes for document modules in {0}ms.");
9097

91-
AddSupertypesForDocumentModules(_toResolve.AsReadOnly(), _state);
98+
ExecuteCompilationPasses(_toResolve.AsReadOnly(), token);
9299
token.ThrowIfCancellationRequested();
93100

94101
parsingStageTimer.Stop();
95-
parsingStageTimer.Log("Added supertypes for document modules in {0}ms.");
102+
parsingStageTimer.Log("Executed compilation passes in {0}ms.");
103+
parsingStageTimer.Restart();
96104

97105
var parseTreesToResolve = _state.ParseTrees.Where(kvp => _toResolve.Contains(kvp.Key)).ToList();
98106
token.ThrowIfCancellationRequested();
@@ -151,90 +159,46 @@ private void ExecuteCompilationPasses(IReadOnlyCollection<QualifiedModuleName> m
151159
}
152160
}
153161

154-
private void AddSupertypesForDocumentModules(IReadOnlyCollection<QualifiedModuleName> modules, RubberduckParserState state)
155-
{
156-
var documentModuleDeclarations = state.DeclarationFinder.UserDeclarations(DeclarationType.Document)
157-
.OfType<DocumentModuleDeclaration>()
158-
.Where(declaration => modules.Contains(declaration.QualifiedName.QualifiedModuleName));
159-
160-
foreach (var documentDeclaration in documentModuleDeclarations)
161-
{
162-
var documentSupertype = SupertypeForDocument(documentDeclaration.QualifiedName.QualifiedModuleName, state);
163-
if (documentSupertype != null)
164-
{
165-
documentDeclaration.AddSupertype(documentSupertype);
166-
}
167-
}
168-
}
162+
// skip IDispatch.. just about everything implements it and RD doesn't need to care about it; don't care about IUnknown either
163+
private static readonly HashSet<string> IgnoredComInterfaces = new HashSet<string>(new[] { "IDispatch", "IUnknown" });
169164

170-
private Declaration SupertypeForDocument(QualifiedModuleName module, RubberduckParserState state)
165+
private void AddSuperTypeNamesForDocumentModules(IReadOnlyCollection<QualifiedModuleName> modules, RubberduckParserState state, IUserComProjectProvider userComProjectProvider)
171166
{
172-
if(module.ComponentType != ComponentType.Document)
173-
{
174-
return null;
175-
}
167+
//todo: Figure out how to unit test this.
176168

177-
var component = _state.ProjectsProvider.Component(module);
178-
if (component == null || component.IsWrappingNullReference)
179-
{
180-
return null;
181-
}
169+
var documentModuleDeclarationsByProject = state.DeclarationFinder.UserDeclarations(DeclarationType.Document)
170+
.OfType<DocumentModuleDeclaration>()
171+
.Where(declaration => modules.Contains(declaration.QualifiedName.QualifiedModuleName))
172+
.GroupBy(declaration => declaration.ProjectId);
182173

183-
Declaration superType = null;
184-
// TODO: Replace with TypeLibAPI call, require a solution regarding thread synchronization or caching
185-
/*
186-
using (var properties = component.Properties)
174+
foreach (var projectGroup in documentModuleDeclarationsByProject)
187175
{
188-
int documentPropertyCount = 0;
189-
try
176+
var userComProject = userComProjectProvider.UserProject(projectGroup.Key);
177+
var documents = projectGroup.ToDictionary(module => module.IdentifierName);
178+
foreach (var comModule in userComProject.Members)
190179
{
191-
if (properties == null || properties.IsWrappingNullReference)
180+
if (!(documents.TryGetValue(comModule.Name, out var document)))
192181
{
193-
return null;
182+
continue;
194183
}
195-
documentPropertyCount = properties.Count;
196-
}
197-
catch(COMException)
198-
{
199-
return null;
200-
}
201-
202-
foreach (var coclass in state.CoClasses)
203-
{
204-
try
205-
{
206-
if (coclass.Key.Count != documentPropertyCount)
207-
{
208-
continue;
209-
}
210-
211-
var allNamesMatch = true;
212-
for (var i = 0; i < coclass.Key.Count; i++)
213-
{
214-
using (var property = properties[i+1])
215-
{
216-
if (coclass.Key[i] != property?.Name)
217-
{
218-
allNamesMatch = false;
219-
break;
220-
}
221-
}
222-
}
223-
224-
if (allNamesMatch)
225-
{
226-
superType = coclass.Value;
227-
break;
228-
}
229-
}
230-
catch (COMException)
184+
185+
var inheritedInterfaces = comModule is ComCoClass documentCoClass
186+
? documentCoClass.ImplementedInterfaces
187+
: (comModule as ComInterface)?.InheritedInterfaces;
188+
189+
//todo: Find a way to deal with the VBE's document type assignment behaviour not relying on an assumption about an interface naming convention.
190+
var superTypeNames = inheritedInterfaces?
191+
.Where(i => !i.IsRestricted && !IgnoredComInterfaces.Contains(i.Name))
192+
.Select(i => i.Name)
193+
.Select(name => name.StartsWith("_") ? name.Substring(1) : name) //This emulates the VBE's behaviour to allow assignment to the coclass type instead on the interface.
194+
?? Enumerable.Empty<string>();
195+
196+
foreach (var superTypeName in superTypeNames)
231197
{
198+
document.AddSupertypeName(superTypeName);
232199
}
233200
}
234201
}
235-
*/
236-
237-
return superType;
238202
}
239203

240204
protected void ResolveReferences(DeclarationFinder finder, QualifiedModuleName module, IParseTree tree, CancellationToken token)

Rubberduck.Parsing/VBA/ReferenceManagement/SynchronousReferenceResolveRunner.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Threading;
44
using Antlr4.Runtime.Tree;
5+
using Rubberduck.Parsing.ComReflection;
56
using Rubberduck.Parsing.VBA.DeclarationCaching;
67
using Rubberduck.VBEditor;
78

@@ -12,13 +13,15 @@ public class SynchronousReferenceResolveRunner : ReferenceResolveRunnerBase
1213
public SynchronousReferenceResolveRunner(
1314
RubberduckParserState state,
1415
IParserStateManager parserStateManager,
15-
IModuleToModuleReferenceManager moduletToModuleReferenceManager,
16-
IReferenceRemover referenceRemover)
16+
IModuleToModuleReferenceManager moduleToModuleReferenceManager,
17+
IReferenceRemover referenceRemover,
18+
IUserComProjectProvider userComProjectProvider)
1719
: base(state,
1820
parserStateManager,
19-
moduletToModuleReferenceManager,
20-
referenceRemover)
21-
{ }
21+
moduleToModuleReferenceManager,
22+
referenceRemover,
23+
userComProjectProvider)
24+
{}
2225

2326

2427
protected override void ResolveReferences(ICollection<KeyValuePair<QualifiedModuleName, IParseTree>> toResolve, CancellationToken token)

RubberduckTests/Mocks/MockParser.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ public static (SynchronousParseCoordinator parser, IRewritingManager rewritingMa
122122
state,
123123
parserStateManager,
124124
moduleToModuleReferenceManager,
125-
referenceRemover);
125+
referenceRemover,
126+
userComProjectsRepository);
126127
var parsingStageService = new ParsingStageService(
127128
comSynchronizer,
128129
builtInDeclarationLoader,

0 commit comments

Comments
 (0)