Search code examples
excelvbarubberduck

Rubberduck VBA Code Inspections: Member 'x' has a 'VB_VarHelpID' attribute with value '-1', but no corresponding annotation


I am developing an Excel VBA project using the "Worksheet abstraction\Worksheet Proxy" technique, described in the There is no worksheet article and followed-up in my question here. My VBA code is structured in the MVP design pattern and I have written as much OOP code as possible. Rubberduck's "Code Inspections" feature has been of great help along the way, however in more recent versions (I think since v2.4.1.4*** but can't exactly put my finger on the exact version) I started to consistently get a couple of "Rubberduck Opportunities" and "Code Quality Issues" warnings that I can't quite make sense of.

The first one, as mentioned in the title, is the Member 'x' has a 'VB_VarHelpID' attribute with value '-1', but no corresponding annotation Rubberduck Opportunity. I get this one whenever I am declaring an event-exposing Worksheet (or other event-exposing object, i.e. a CommandButton) inside a "WorksheetProxy" class. Both lines in the below code would trigger this error:

' This code sits in my ProcessMasterProxy class
Private WithEvents sheet As Worksheet
Private WithEvents buttonHideOwnerToAvailability As CommandButton

Then I would get the same error in my "Presenter" class whenever I declare an instance of an event-exposing "SheetProxy" class, or an event-exposing UserForm:

' Here I am declaring an instance of the ProcessMasterProxy class from the above snippet
Private WithEvents assetPrx As ProcessMasterProxy
Private WithEvents view As ChecklistPopup

Rubberduck's Code Inspections offers me two actions for such errors: 1. "Add attribute annotation" which inserts the '@MemberAttribute VB_VarHelpID, -1 line above the declaration AND 2. "Remove attribute" which seems to do nothing, the error remains after clicking it.

I would like to know what implications does the "Add attribute annotation" fix bear on my VBA project and whether I should apply it or rather look to change something in my code to avoid getting the error altogether.

Similarly, I am concerned about a related error I get in the "Code Quality Issues" section: To the variable 'sheet' of declared type 'EXCEL.EXE:Excel.Worksheet' a value is set assigned with the incompatible declared type 'ProcessMgmt.ProcessMaster' which is related to the following code in the "ProcessMasterProxy" class from the first snippet above:

Private WithEvents sheet As Worksheet
Private WithEvents buttonHideOwnerToAvailability As CommandButton

Private Sub Class_Initialize()
    ***Set sheet = ProcessMaster***
    Set buttonHideOwnerToAvailability = ProcessMaster.btnHideAssetOwnerToAvailability
End Sub

with "ProcessMaster" being the name of the actual Excel Worksheet and the above code sitting inside its corresponding "SheetProxy" Class.

The only option offered by Code Inspections for this error is "Ignore once" and again I would like to know whether it is safe to do that. I have had this errors appear in a couple of projects before, yet everything worked fine there. However, in my most recent project I started randomly getting a “Run-time Error 35010” when opening the workbook, where I have the following code in my Workbook_Open event:

Private pres As Presenter
If pres Is Nothing Then
    Set pres = New Presenter
End If

Could this problem be related to any of the above two Code Inspections suggestions/errors?


Solution

  • The role of these inspections is to surface hidden attributes that the VBE might be adding. Often these attributes affect how a class can be used (VB_Exposed, VB_PredeclaredId), or how a member behaves (VB_UserMemId, etc.). But they are also completely invisible in the editor, and without a corresponding annotation/comment, it's often impossible to tell their presence.

    inspection result for "Missing member annotation"

    In this particular case Rubberduck is informing you1 that there's a hidden VB_VarHelpId attribute on the WithEvents variable.

    By adding the corresponding annotation comment, we're making hidden code visible and modifiable in the editor: change the annotation's argument value, and Rubberduck inspections will now say attributes and annotations aren't synchronized, which means the hidden code says one thing but the visible comment is saying another.

    Removing the attribute should have had the effect of exporting the module to a temporary file, modifying that file to remove the hidden attribute, and reimporting the modified module into the project. Note that because document modules (e.g. ThisWorkbook, or sheet modules) cannot be imported into the project that way, this doesn't work in document modules, so Rubberduck shouldn't warn about desynchronized annotations/attributes in these modules. If the quickfix didn't do anything, please report a bug, because this should definitely work! (edit: confirmed working as intended with build 2.4.1.5229)

    Bottom line, "Rubberduck Opportunities" inspections are just that: opportunities to leverage Rubberduck-specific features (like annotation comments managing hidden attributes): they aren't indicative of anything wrong with the code.


    The "incompatible type" inspection result is a known issue: at the moment Rubberduck is not seeing the Worksheet interface of worksheet modules (nor the Workbook interface of ThisWorkbook), and this is causing a number of false positives around document modules. Until Rubberduck can tell that ProcessMaster is not just a ProcessMaster object but also a Worksheet (we'll have a fix for that in 2.5.x), it's probably safe to ignore that inspection when it's complaining about document modules and MSForms interfaces (which have the same underlying issue).

    The idea behind this inspection, is that often VBA will only realize at run-time that object types aren't compatible, but (assuming it sees all interfaces of all types) Rubberduck can tell you about a problem at design-time, well ahead of execution.


    As for the intermittent error, I suspect it's because you are accessing objects before they are actually available to use. The Initialize handler of your class runs at the New Presenter statement, before the call returns with the reference to assign pres.

    I'd try to see if moving the code from the Initialize handler and into some parameterized initializer procedure would fix it.

    If pres Is Nothing Then
        Set pres = New Presenter
        pres.Initialize ProcessMaster
    End If
    

    Where this Initialize procedure might look like this (untested):

    Public Sub Initialize(ByVal masterSheet As Worksheet)
        Set sheet = masterSheet
        Set buttonHideOwnerToAvailability = masterSheet.Buttons("btnHideAssetOwnerToAvailability")
    End Sub
    

    The idea being, to inject the worksheet dependency rather than couple it to the presenter class (that's a very good thing!), but mostly to let the class instance initialize itself fully before we start accessing the host document (which has just opened and may have some asynchronous initializations that have yet to complete... just thinking out loud here, this may or may not be what's actually going on).


    1 The severity level of each individual inspection can be configured in the settings.