Skip to content

Resolving Late-Bound Member Calls (kinda) #4925

Open
@retailcoder

Description

@retailcoder

Justification
Right now when the resolver encounters a member it can't find a Declaration for, it logs a binding failure and attempts to resolve known identifiers in the rest of the expression. This makes late-bound member calls unresolvable:

Dim foo As Range
Set foo = ActiveWorkbook.Worksheets("Sheet1").Range(cellAddress)

Here Range is a late-bound member call made against Object, but assuming cellAddress is some local variable, that's all Rubberduck will be seeing to the right of the Worksheets member call.

Note: this is a discussion, not a spec.

Description
Similar to how we create on-the-fly declarations for undeclared variables, we could be creating on-the-fly declarations for late-bound or otherwise unknown members. Rather than adding a new flag to the already-overcrowded Declaration class, let's broaden the usage of the IsUndeclared property, which we already use for undeclared variables.

I suggest we create such a declaration whenever we encounter a member we can't resolve:

Dim foo As Class1 ' a known class module
Set foo = New Class1 
foo.Bar = 42 ' member "Bar" isn't found in module "Class1" (won't compile)

So here we would be adding an "undeclared" member Bar under Class1, and as a first iteration its data type could be Variant, and then that can be enhanced later to infer a data type from usage.

Another one:

Dim foo As SomeClass ' non-existing class module
Set foo = New SomeClass ' won't compile
foo.DoSomething 42

Here we would be adding an "undeclared" SomeClass class, a DoSomething procedure (Function would be the easiest for a first iteration), and one Variant (or as inferred from usage) parameter declaration under it.

And in the case of late-bound code:

Dim outlookApp As Object
Set outlookApp = CreateObject("Outlook.Application")
outlookApp.Quit

Here we would create a class declaration with a Quit method. The name of the bogus class could be outlookAppClass (based on the identifier for the object of that type), and the parent project would have to be the containing project.

When the late-bound member is against a known member, the parent project needs to be whatever project/library declares the deepest known member:

Dim foo As Range
Set foo = ActiveWorkbook.Worksheets("Sheet1").Range(cellAddress)

So here we would create an undeclared WorksheetsClass declaration under the Excel library, and an undeclared Range function under it, taking a single parameter.

In a dream world, we would have the resolver take note of how an Object-returning member is used, e.g.:

Dim ws As Worksheet
Set ws = ActiveWorkbook.Worksheets(sheetName)

Dim wss As Sheets
Set wss = ActiveWorkbook.Worksheets(Array(sheetName1, sheetName2))

And then annotate the WorksheetsClass undeclared type with a resolver hint that says "this type can return a Worksheet or Sheets object" -- and then pipe late-bound resolution through these hints; and then in the case of code like this:

Worksheets("Sheet1").Range("A1") = 42

...we could successfully resolve the Range member call to the Excel.Worksheet.Range declaration, with a new IdentifierReference.IsLateBound flag turned on.


One significant enhancement this would enable in the future, would be intellisense for late-bound member calls and not-yet-created types, a bit like R# does: we could then have a quickfix (?) that says "Generate class" and/or "Generate member".

This is very much a gigantic can of worms with tons of possibly unintended consequences, both good and bad. Thoughts? Other ideas? Let's think of what the consequences are, and decide whether we can/should go down this path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    discussionenhancementFeature requests, or enhancements to existing features. Ideas. Anything within the project's scope.resolverIssue is easier to resolve with knowledge of the internal resolver API and the Antlr4 parse trees.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions