In order for Rubberduck to be able to "recognize" the built-in VBA functions, procedures and objects, I added yet another constructor parameter to my Declaration
class:
public Declaration(QualifiedMemberName qualifiedName, string parentScope,
string identifierName, string asTypeName, bool isSelfAssigned, bool isWithEvents,
Accessibility accessibility, DeclarationType declarationType, ParserRuleContext context, Selection selection, bool isBuiltIn = false)
{
_qualifiedName = qualifiedName;
_parentScope = parentScope;
_identifierName = identifierName;
_asTypeName = asTypeName;
_isSelfAssigned = isSelfAssigned;
_isWithEvents = isWithEvents;
_accessibility = accessibility;
_declarationType = declarationType;
_selection = selection;
_context = context;
_isBuiltIn = isBuiltIn;
}
That constructor is becoming a real mess, but now I can tell built-in declarations from the rest - this way I can know if the user is trying to rename the MsgBox
function, and forbid it instead of happily letting the user break their code; as a bonus I can now also map identifier usages to these declarations, so the user can right-click any MsgBox
function call, and I can show them everywhere they're using it in their VBA project.
So I proceeded to implement a little helper class that would give me an IEnumerable<Declaration>
containing everything the VBA Standard Library exposes.
Because I'm only ever going to need to instantiate these declarations once, I decided to make it a static
class, exposing nothing but a Declarations
property getter.
At first I had a #region
for each predefined module and class, and another for the predefined enums.. but I don't like #region
, so I decided to use private nested types instead, and to define the Declaration
instances as public static fields, so that I could easily use reflection to fetch all instances from all nested types, like this:
/// <summary>
/// Defines <see cref="Declaration"/> objects for the standard library.
/// </summary>
internal static class VbaStandardLib
{
private static readonly QualifiedModuleName EmptyModuleName = new QualifiedModuleName();
private static IEnumerable<Declaration> StandardLibDeclarations;
public static IEnumerable<Declaration> Declarations
{
get
{
return StandardLibDeclarations ??
(StandardLibDeclarations = typeof (VbaStandardLib).GetNestedTypes(BindingFlags.NonPublic)
.SelectMany(t => t.GetFields().Select(f => (Declaration) f.GetValue(null))));
}
}
Here are a few of these nested types, just to illustrate what they look like:
private class VbaLib
{
public static Declaration Vba = new Declaration(new QualifiedMemberName(EmptyModuleName, "VBA"), "VBA", "VBA", "VBA", true, false, Accessibility.Global, DeclarationType.Project, null, Selection.Home, true);
public static Declaration FormShowConstants = new Declaration(new QualifiedMemberName(EmptyModuleName, "FormShowConstants"), "VBA", "FormShowConstants", "FormShowConstants", false, false, Accessibility.Global, DeclarationType.Enumeration, null, Selection.Home, true);
public static Declaration VbModal = new Declaration(new QualifiedMemberName(EmptyModuleName, "vbModal"), "VBA", "vbModal", "FormShowConstants", true, false, Accessibility.Global, DeclarationType.EnumerationMember, null, Selection.Home, true);
public static Declaration VbModeless = new Declaration(new QualifiedMemberName(EmptyModuleName, "vbModeless"), "VBA", "vbModeless", "FormShowConstants", true, false, Accessibility.Global, DeclarationType.EnumerationMember, null, Selection.Home, true);
public static Declaration VbAppWinStyle = new Declaration(new QualifiedMemberName(EmptyModuleName, "VbAppWinStyle"), "VBA", "VbAppWinStyle", "VbAppWinStyle", false, false, Accessibility.Global, DeclarationType.Enumeration, null, Selection.Home, true);
public static Declaration VbHide = new Declaration(new QualifiedMemberName(EmptyModuleName, "vbHide"), "VBA", "vbHide", "VbAppWinStyle", true, false, Accessibility.Global, DeclarationType.EnumerationMember, null, Selection.Home, true);
public static Declaration VbMaximizedFocus = new Declaration(new QualifiedMemberName(EmptyModuleName, "vbMaximizedFocus"), "VBA", "vbMaximizedFocus", "VbAppWinStyle", true, false, Accessibility.Global, DeclarationType.EnumerationMember, null, Selection.Home, true);
public static Declaration VbMinimizedFocus = new Declaration(new QualifiedMemberName(EmptyModuleName, "vbMinimizedFocus"), "VBA", "vbMinimizedFocus", "VbAppWinStyle", true, false, Accessibility.Global, DeclarationType.EnumerationMember, null, Selection.Home, true);
public static Declaration VbMinimizedNoFocus = new Declaration(new QualifiedMemberName(EmptyModuleName, "vbMinimizedNoFocus"), "VBA", "vbMinimizedNoFocus", "VbAppWinStyle", true, false, Accessibility.Global, DeclarationType.EnumerationMember, null, Selection.Home, true);
public static Declaration VbNormalFocus = new Declaration(new QualifiedMemberName(EmptyModuleName, "vbNormalFocus"), "VBA", "vbNormalFocus", "VbAppWinStyle", true, false, Accessibility.Global, DeclarationType.EnumerationMember, null, Selection.Home, true);
public static Declaration VbNormalNoFocus = new Declaration(new QualifiedMemberName(EmptyModuleName, "vbNormalNoFocus"), "VBA", "vbNormalNoFocus", "VbAppWinStyle", true, false, Accessibility.Global, DeclarationType.EnumerationMember, null, Selection.Home, true);
...
I've regrouped the members so as to mirror the structure of the VBA
standard library, so the declarations contained in the VBA.Information
module are defined in the InformationModule
class:
private class InformationModule
{
public static Declaration Information = new Declaration(new QualifiedMemberName(EmptyModuleName, "Information"), "VBA", "Information", "Information", false, false, Accessibility.Global, DeclarationType.Module, null, Selection.Home, true);
public static Declaration Err = new Declaration(new QualifiedMemberName(EmptyModuleName, "Err"), "VBA.Information", "Err", "ErrObject", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration IMEStatus = new Declaration(new QualifiedMemberName(EmptyModuleName, "IMEStatus"), "VBA.Information", "IMEStatus", "vbIMEStatus", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration IsArray = new Declaration(new QualifiedMemberName(EmptyModuleName, "IsArray"), "VBA.Information", "IsArray", "Boolean", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration IsDate = new Declaration(new QualifiedMemberName(EmptyModuleName, "IsDate"), "VBA.Information", "IsDate", "Boolean", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration IsEmpty = new Declaration(new QualifiedMemberName(EmptyModuleName, "IsEmpty"), "VBA.Information", "IsEmpty", "Boolean", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration IsError = new Declaration(new QualifiedMemberName(EmptyModuleName, "IsError"), "VBA.Information", "IsError", "Boolean", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration IsMissing = new Declaration(new QualifiedMemberName(EmptyModuleName, "IsMissing"), "VBA.Information", "IsMissing", "Boolean", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration IsNull = new Declaration(new QualifiedMemberName(EmptyModuleName, "IsNull"), "VBA.Information", "IsNull", "Boolean", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration IsNumeric = new Declaration(new QualifiedMemberName(EmptyModuleName, "IsNumeric"), "VBA.Information", "IsNumeric", "Boolean", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration IsObject = new Declaration(new QualifiedMemberName(EmptyModuleName, "IsObject"), "VBA.Information", "IsObject", "Boolean", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration QBColor = new Declaration(new QualifiedMemberName(EmptyModuleName, "QBColor"), "VBA.Information", "QBColor", "Long", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration RGB = new Declaration(new QualifiedMemberName(EmptyModuleName, "RGB"), "VBA.Information", "RGB", "Long", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration TypeName = new Declaration(new QualifiedMemberName(EmptyModuleName, "TypeName"), "VBA.Information", "TypeName", "String", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
public static Declaration VarType = new Declaration(new QualifiedMemberName(EmptyModuleName, "VarType"), "VBA.Information", "vbVarType", "Boolean", false, false, Accessibility.Global, DeclarationType.Function, null, Selection.Home, true);
}
There are quite a few more, listing them here would be irrelevant.
Should I restructure this? This code isn't really going to be maintained, unless the VBA
standard library suddenly earns new members... on the other hand, I might eventually decide to implement similar classes for commonly referenced VBA libraries, such as Scripting
and ADODB
- any thoughts?