-
Notifications
You must be signed in to change notification settings - Fork 296
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'next' of https://github.com/rubberduck-vba/Rubberduck i…
…nto Issue4149_TestExplorerFilter
- Loading branch information
Showing
3 changed files
with
11 additions
and
293 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
298 changes: 7 additions & 291 deletions
298
Rubberduck.Parsing/ComReflection/ReferencedDeclarationsCollector.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,309 +1,25 @@ | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Runtime.InteropServices.ComTypes; | ||
using Rubberduck.Parsing.Symbols; | ||
using Rubberduck.VBEditor; | ||
|
||
namespace Rubberduck.Parsing.ComReflection | ||
{ | ||
public abstract class ReferencedDeclarationsCollectorBase : IReferencedDeclarationsCollector | ||
{ | ||
public abstract IReadOnlyCollection<Declaration> CollectedDeclarations(ReferenceInfo reference); | ||
private readonly IDeclarationsFromComProjectLoader _declarationsFromComProjectLoader; | ||
|
||
protected List<Declaration> LoadDeclarationsFromComProject(ComProject type) | ||
protected ReferencedDeclarationsCollectorBase(IDeclarationsFromComProjectLoader declarationsFromComProjectLoader) | ||
{ | ||
var declarations = new List<Declaration>(); | ||
|
||
var projectName = new QualifiedModuleName(type.Name, type.Path, type.Name); | ||
var project = new ProjectDeclaration(type, projectName); | ||
declarations.Add(project); | ||
|
||
foreach (var alias in type.Aliases.Select(item => new AliasDeclaration(item, project, projectName))) | ||
{ | ||
declarations.Add(alias); | ||
} | ||
|
||
foreach (var module in type.Members) | ||
{ | ||
var moduleIdentifier = module.Type == DeclarationType.Enumeration || module.Type == DeclarationType.UserDefinedType | ||
? $"_{module.Name}" | ||
: module.Name; | ||
var moduleName = new QualifiedModuleName(type.Name, type.Path, moduleIdentifier); | ||
|
||
var moduleDeclarations = GetDeclarationsForModule(module, moduleName, project); | ||
declarations.AddRange(moduleDeclarations); | ||
} | ||
|
||
return declarations; | ||
_declarationsFromComProjectLoader = declarationsFromComProjectLoader; | ||
} | ||
|
||
private static ICollection<Declaration> GetDeclarationsForModule(IComType module, QualifiedModuleName moduleName, ProjectDeclaration project) | ||
{ | ||
var declarations = new List<Declaration>(); | ||
|
||
var attributes = GetModuleAttributes(module); | ||
var moduleDeclaration = CreateModuleDeclaration(module, moduleName, project, attributes); | ||
declarations.Add(moduleDeclaration); | ||
|
||
switch (module) | ||
{ | ||
case IComTypeWithMembers membered: | ||
var (memberDeclarations, defaultMember) = | ||
GetDeclarationsForProperties(membered.Properties, moduleName, moduleDeclaration); | ||
declarations.AddRange(memberDeclarations); | ||
AssignDefaultMember(moduleDeclaration, defaultMember); | ||
|
||
(memberDeclarations, defaultMember) = GetDeclarationsForMembers(membered.Members, moduleName, | ||
moduleDeclaration, membered.DefaultMember); | ||
declarations.AddRange(memberDeclarations); | ||
AssignDefaultMember(moduleDeclaration, defaultMember); | ||
|
||
if (membered is ComCoClass coClass) | ||
{ | ||
(memberDeclarations, defaultMember) = GetDeclarationsForMembers(coClass.SourceMembers, | ||
moduleName, moduleDeclaration, coClass.DefaultMember, true); | ||
declarations.AddRange(memberDeclarations); | ||
AssignDefaultMember(moduleDeclaration, defaultMember); | ||
} | ||
|
||
break; | ||
case ComEnumeration enumeration: | ||
{ | ||
var enumDeclaration = new Declaration(enumeration, moduleDeclaration, moduleName); | ||
declarations.Add(enumDeclaration); | ||
var members = enumeration.Members | ||
.Select(enumMember => new ValuedDeclaration(enumMember, enumDeclaration, moduleName)) | ||
.ToList(); | ||
declarations.AddRange(members); | ||
break; | ||
} | ||
case ComStruct structure: | ||
{ | ||
var typeDeclaration = new Declaration(structure, moduleDeclaration, moduleName); | ||
declarations.Add(typeDeclaration); | ||
var members = structure.Fields | ||
.Select(f => new Declaration(f, typeDeclaration, moduleName)) | ||
.ToList(); | ||
declarations.AddRange(members); | ||
break; | ||
} | ||
} | ||
|
||
if (module is IComTypeWithFields fields && fields.Fields.Any()) | ||
{ | ||
var projectName = project.QualifiedModuleName; | ||
var fieldDeclarations = new List<Declaration>(); | ||
foreach (var field in fields.Fields) | ||
{ | ||
fieldDeclarations.Add(field.Type == DeclarationType.Constant | ||
? new ValuedDeclaration(field, moduleDeclaration, projectName) | ||
: new Declaration(field, moduleDeclaration, projectName)); | ||
} | ||
|
||
declarations.AddRange(fieldDeclarations); | ||
} | ||
|
||
return declarations; | ||
} | ||
|
||
private static void AssignDefaultMember(Declaration moduleDeclaration, Declaration defaultMember) | ||
{ | ||
if (defaultMember != null && moduleDeclaration is ClassModuleDeclaration classDeclaration) | ||
{ | ||
classDeclaration.DefaultMember = defaultMember; | ||
} | ||
} | ||
|
||
private static Attributes GetModuleAttributes(IComType module) | ||
{ | ||
var attributes = new Attributes(); | ||
if (module.IsPreDeclared) | ||
{ | ||
attributes.AddPredeclaredIdTypeAttribute(); | ||
} | ||
if (module.IsAppObject) | ||
{ | ||
attributes.AddGlobalClassAttribute(); | ||
} | ||
if (module is IComTypeWithMembers members && members.IsExtensible) | ||
{ | ||
attributes.AddExtensibleClassAttribute(); | ||
} | ||
return attributes; | ||
} | ||
|
||
private static (ICollection<Declaration> memberDeclarations, Declaration defaultMemberDeclaration) GetDeclarationsForMembers(IEnumerable<ComMember> members, QualifiedModuleName moduleName, Declaration moduleDeclaration, | ||
ComMember defaultMember, bool eventHandlers = false) | ||
{ | ||
var memberDeclarations = new List<Declaration>(); | ||
Declaration defaultMemberDeclaration = null; | ||
|
||
foreach (var item in members.Where(m => !m.IsRestricted && !IgnoredInterfaceMembers.Contains(m.Name))) | ||
{ | ||
var (memberDeclaration, parameterDeclarations) = GetDeclarationsForMember(moduleName, moduleDeclaration, eventHandlers, item); | ||
memberDeclarations.Add(memberDeclaration); | ||
memberDeclarations.AddRange(parameterDeclarations); | ||
|
||
if (moduleDeclaration is ClassModuleDeclaration && item == defaultMember) | ||
{ | ||
defaultMemberDeclaration = memberDeclaration; | ||
} | ||
} | ||
|
||
return (memberDeclarations, defaultMemberDeclaration); | ||
} | ||
|
||
private static (Declaration memberDeclaration, List<Declaration> parameterDeclarations) GetDeclarationsForMember(QualifiedModuleName moduleName, | ||
Declaration parentDeclaration, bool eventHandlers, ComMember item) | ||
{ | ||
var memberDeclaration = CreateMemberDeclaration(item, moduleName, parentDeclaration, eventHandlers); | ||
|
||
var parameterDeclarations = new List<Declaration>(); | ||
if (memberDeclaration is IParameterizedDeclaration hasParams) | ||
{ | ||
parameterDeclarations.AddRange(hasParams.Parameters); | ||
} | ||
|
||
return (memberDeclaration, parameterDeclarations); | ||
} | ||
|
||
private static (ICollection<Declaration> propertyDeclarations, Declaration propertyMemberDeclaration) GetDeclarationsForProperties( | ||
IEnumerable<ComField> properties, QualifiedModuleName moduleName, Declaration moduleDeclaration) | ||
{ | ||
var propertyDeclarations = new List<Declaration>(); | ||
Declaration defaultMemberDeclaration = null; | ||
|
||
foreach (var item in properties.Where(x => !x.Flags.HasFlag(VARFLAGS.VARFLAG_FRESTRICTED))) | ||
{ | ||
Debug.Assert(item.Type == DeclarationType.Property); | ||
var attributes = GetPropertyAttibutes(item); | ||
var (getter, writer) = GetDeclarationsForProperty(moduleName, moduleDeclaration, item, attributes); | ||
|
||
propertyDeclarations.Add(getter); | ||
if (writer != null) | ||
{ | ||
propertyDeclarations.Add(writer); | ||
} | ||
|
||
if (moduleDeclaration is ClassModuleDeclaration && attributes.HasDefaultMemberAttribute()) | ||
{ | ||
defaultMemberDeclaration = getter; | ||
} | ||
} | ||
|
||
return (propertyDeclarations, defaultMemberDeclaration); | ||
} | ||
|
||
private static (Declaration getter, Declaration writer) GetDeclarationsForProperty(QualifiedModuleName moduleName, Declaration moduleDeclaration, ComField item, Attributes attributes) | ||
{ | ||
var getter = new PropertyGetDeclaration(item, moduleDeclaration, moduleName, attributes); | ||
|
||
if (item.Flags.HasFlag(VARFLAGS.VARFLAG_FREADONLY)) | ||
{ | ||
return (getter, null); | ||
} | ||
|
||
if (item.IsReferenceType) | ||
{ | ||
var setter = new PropertySetDeclaration(item, moduleDeclaration, moduleName, attributes); | ||
return (getter, setter); | ||
} | ||
|
||
var letter = new PropertyLetDeclaration(item, moduleDeclaration, moduleName, attributes); | ||
return (getter, letter); | ||
} | ||
|
||
private static Declaration CreateModuleDeclaration(IComType module, QualifiedModuleName project, Declaration parent, Attributes attributes) | ||
{ | ||
switch (module) | ||
{ | ||
case ComEnumeration enumeration: | ||
return new ProceduralModuleDeclaration(enumeration, parent, project); | ||
case ComStruct types: | ||
return new ProceduralModuleDeclaration(types, parent, project); | ||
case ComCoClass coClass: | ||
return new ClassModuleDeclaration(coClass, parent, project, attributes); | ||
case ComInterface intrface: | ||
return new ClassModuleDeclaration(intrface, parent, project, attributes); | ||
default: | ||
return new ProceduralModuleDeclaration(module as ComModule, parent, project, attributes); | ||
} | ||
} | ||
|
||
private static Declaration CreateMemberDeclaration(ComMember member, QualifiedModuleName module, Declaration parent, bool handler) | ||
{ | ||
var attributes = GetMemberAttibutes(member); | ||
switch (member.Type) | ||
{ | ||
case DeclarationType.Procedure: | ||
return new SubroutineDeclaration(member, parent, module, attributes, handler); | ||
case DeclarationType.Function: | ||
return new FunctionDeclaration(member, parent, module, attributes); | ||
case DeclarationType.Event: | ||
return new EventDeclaration(member, parent, module, attributes); | ||
case DeclarationType.PropertyGet: | ||
return new PropertyGetDeclaration(member, parent, module, attributes); | ||
case DeclarationType.PropertySet: | ||
return new PropertySetDeclaration(member, parent, module, attributes); | ||
case DeclarationType.PropertyLet: | ||
return new PropertyLetDeclaration(member, parent, module, attributes); | ||
default: | ||
// ReSharper disable once LocalizableElement | ||
throw new InvalidEnumArgumentException($"Unexpected DeclarationType {member.Type} encountered."); | ||
} | ||
} | ||
|
||
private static Attributes GetMemberAttibutes(ComMember member) | ||
{ | ||
var attributes = new Attributes(); | ||
if (member.IsEnumerator) | ||
{ | ||
attributes.AddEnumeratorMemberAttribute(member.Name); | ||
} | ||
else if (member.IsDefault) | ||
{ | ||
attributes.AddDefaultMemberAttribute(member.Name); | ||
} | ||
else if (member.IsHidden) | ||
{ | ||
attributes.AddHiddenMemberAttribute(member.Name); | ||
} | ||
else if (member.IsEvaluateFunction) | ||
{ | ||
attributes.AddEvaluateMemberAttribute(member.Name); | ||
} | ||
else if (!string.IsNullOrEmpty(member.Documentation.DocString)) | ||
{ | ||
attributes.AddMemberDescriptionAttribute(member.Name, member.Documentation.DocString); | ||
} | ||
return attributes; | ||
} | ||
public abstract IReadOnlyCollection<Declaration> CollectedDeclarations(ReferenceInfo reference); | ||
|
||
private static Attributes GetPropertyAttibutes(ComField property) | ||
{ | ||
var attributes = new Attributes(); | ||
if (property.Flags.HasFlag(VARFLAGS.VARFLAG_FDEFAULTBIND)) | ||
{ | ||
attributes.AddDefaultMemberAttribute(property.Name); | ||
} | ||
if (property.Flags.HasFlag(VARFLAGS.VARFLAG_FHIDDEN)) | ||
{ | ||
attributes.AddHiddenMemberAttribute(property.Name); | ||
} | ||
return attributes; | ||
} | ||
|
||
private static readonly HashSet<string> IgnoredInterfaceMembers = new HashSet<string> | ||
protected IReadOnlyCollection<Declaration> LoadDeclarationsFromComProject(ComProject type, string projectId = null) | ||
{ | ||
"QueryInterface", | ||
"AddRef", | ||
"Release", | ||
"GetTypeInfoCount", | ||
"GetTypeInfo", | ||
"GetIDsOfNames", | ||
"Invoke" | ||
}; | ||
return _declarationsFromComProjectLoader.LoadDeclarations(type, projectId); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters