rubberduck-vba / Rubberduck

Every programmer needs a rubberduck. COM add-in for the VBA & VB6 IDE (VBE).
https://rubberduckvba.com
GNU General Public License v3.0
1.92k stars 302 forks source link

Refactor bang operators into dot operators #5093

Open bclothier opened 5 years ago

bclothier commented 5 years ago

Justification When replying to #5092 I thought we already had a request for this refactoring but was surprised that there wasn't.

The proposal is to have a simple refactor that converts all bang operators into dot operators -- there is no legit reason to use bang operators, especially on Me on the LHS. The refactor should expand the expressions (e.g. Access' form's Me!Foo should become Me.Foo which is more descriptive of what the code is actually doing and more importantly makes the code verifiable.

An unverifiable code is unrefactorable code. Rubberduck should help them write verifiable code.

Description Given:

Assuming an Access form and a DAO Recordset:

Me!Foo = rs!Bar 

The outcome should be:

Me.Foo.Value = rs.Fields("Bar").Value

Note that the Access form can have dot access to the controls without going through the Me.Controls collection, but we can't do that for the recordset. That may require some inspection on the object to determine whether we can dot access the member or use the default collection.

bclothier commented 5 years ago

Addendum: the userforms also have the same behavior so it is obvious that there is a feature that enables the container to support member access via dot operator to the collection members. For the refactoring to be most productive, we'll need to be able to recognize when the container (be it Access Form/Report or Userform) can make dotted access and refactor toward that over the collection unlike the case of recordsets.

retailcoder commented 5 years ago

Wouldn't that just be the default member? UserForm.Controls and Recordset.Fields aren't all that different.

bclothier commented 5 years ago

Both are default members, yes but last time I checked rs.SomeField is a compile error whereas frm.SomeControl isn't. In the latter case, it's obviously doing some extra to enable the dot access on the member (e.g. adding the controls to the interface dynamically). Recordsets never exhibit that behavior. In the cases of forms/reports/userforms, I suspect that being persisted, they can dynamically update the interface and thus expose the members directly on the interface, whereas recordsets and other similar objects can't since they are always created at the runtime -- there is no way of knowing what shape they'll have until then and it's already too late.


Just for references, two things to investigate that may or may not be relevant to this:

defaultcollelem attribute which is labeled as "Visual Basic optimization. Not sure if the interfaces get decorated this way, which would then enable the dot access?

IVBGetControl::EnumControls method If the object implements this interface that might be also what enables the dot access?

bclothier commented 5 years ago

A counterexample --

    Dim c As ADODB.Connection
    Set c = New ADODB.Connection
    c.foo

This compiles (but yield a runtime error unless I actually connect to some source that has foo as a procedure or something. However, unlike the frm.SomeControl case, there is no intellisense downstream from the foo. Note that by default, all COM interfaces are extensible -- to disallow dot access on random members, the interface must use the nonextensible attribute, which is the case with DAO.Recordset & ADODB.Recordset and thus why it will yield a compile error, but not on ADODB.Connection.

MDoerner commented 5 years ago

Sorry, I linked the wrong issue in the PR closing this. The part about controls is still open.