Rubberduck: Provide assistance locating/loading the correct Reference library

Created on 5 Mar 2020  路  4Comments  路  Source: rubberduck-vba/Rubberduck

Justification
If a user copies code or imports a module from file, the code may require reference libraries in order to compile/run. The right library to add is not always obvious as is demonstrated by this SO question that seems to be very popular (I wish I had asked it).

Description
Using the SO question as an example: Assume the imported module file contains Dim fso As FileSystemObject. When the user attempts to run the code or compile it, the following error is presented.

User-defined type not defined

Now the user has to _figure out_ what reference library to add (search=>end up at SO=>UpVote=>back to coding). It would be really great if RD could assist in identifying the necessary library. Right-mouse-click on FileSystemObject and get an option to add the Scripting Runtime library. Or, if this compile error can be found during the Parse...offer to add the Reference Library after the Parse is completed.

Additional context
I'm not sure if this is even possible or reasonable as the number of libraries is enormous. Still, supporting even a small subset could be useful since there are probably a number of _typically_ added libraries (like Scripting Runtime) that RD could help with.

enhancement

Most helpful comment

We could have a .yaml dictionary of type names and libraries they are defined in:

ClassIdentifier,LibraryIdentifier,ReferencePath
FileSystemObject,Scripting,{path to scrrun.dll}
File,Scripting,{path to scrrun.dll}
...
Range,Excel,{path to Excel library}
Range,Word,{path to Word library}
...

And when we encounter unresolved types, we could pull a list of "suggestions" from this dictionary, and then suggest Excel.Range and Word.Range given an unresolved Range type; user picks the one they want, and we add the corresponding library reference - just like in VS with R#!

We just need a way to output such a list from in-memory references, and then it could be loaded at startup and cached.

All 4 comments

This is kind of a non-trivial problem. In most code samples they fail to use two part naming. Thus we only have Dim fso As FileSystemObject, which tells us nothing about from which library it comes from. Had it been Dim fso As Scripting.FileSystemObject, then we would be able to locate it a bit easily.

The other problem is the fact that name and ProgID don't always concide. Consider:

Dim regex As VBScript_RegExp_55.RegExp
Set regex = CreateObject("VBScript.RegExp") 
'WTF why is it "VBScript" but not "VBScript_RegExp_55"?

In order to make it possible, we'd have to trawl the entire registry hive and extract all possible ProgIDs, build a list of all tokens and the type library it came from, then try to match the name to one. That way, the user can get "possible matches". That parse would need to be run only once in a while (not necessarily as part of the code parsing run) since the list don't change unless there has been installations or uninstallations. However, we get into complications since we need a way to refresh that cache of possible ProgIDs after such changes.

To compound the problem, not all objects are exposed with ProgID or are creatable in this manner. Off the cuff, I don't think you can create a Excel.Worksheet but you can a Excel.Workbook; therefore, the users need to know _which_ variable to check for possible reference. A better UX would be to immediately detect the missing libraries as soon as they paste the code, like in Visual Studio (maybe with R#'s help), so the user doesn't have to go on an easter hunt to find which variables is the right one to add reference. But what if they paste a code that didn't create the variable but take a reference? That may fail entirely because the types are not creatable via CreateObject and the code pasted assumes that it's coming from somewhere else.

We could have a .yaml dictionary of type names and libraries they are defined in:

ClassIdentifier,LibraryIdentifier,ReferencePath
FileSystemObject,Scripting,{path to scrrun.dll}
File,Scripting,{path to scrrun.dll}
...
Range,Excel,{path to Excel library}
Range,Word,{path to Word library}
...

And when we encounter unresolved types, we could pull a list of "suggestions" from this dictionary, and then suggest Excel.Range and Word.Range given an unresolved Range type; user picks the one they want, and we add the corresponding library reference - just like in VS with R#!

We just need a way to output such a list from in-memory references, and then it could be loaded at startup and cached.

Ok that gives me an idea. We can use the startup as the point to refresh the cache and we can set it up so that when we evaluate the reference we check if the cache already has the type library & version. If so, skip that and move to next type library. That might be faster after the initial caching. Since those are type libraries, it should be possible do all this asynchronously in a background threading. When we find a type library (or a different version) that cache doesn't have, we will dig in and populate the types (which is going to be the most expensive part of the refresh).

Note, above was just plain CSV, but any format will do. CSV might actually be the simplest and easiest for users to tweak...

Dictionary,MyVBATools,{path to .xlsm}
List,MyVBATools,{path to .xlsm}
...
Was this page helpful?
0 / 5 - 0 ratings

Related issues

retailcoder picture retailcoder  路  3Comments

susnick picture susnick  路  3Comments

philippetev picture philippetev  路  3Comments

Hosch250 picture Hosch250  路  3Comments

Inarion picture Inarion  路  3Comments