Rubberduck: RD needs an inspection for multiple declarations of the same named argument.

Created on 16 Sep 2016  路  9Comments  路  Source: rubberduck-vba/Rubberduck

Apparently VBA doesn't care if you give a parameter for the same argument multiple times:

Sub Foo()
    Debug.Print WorksheetFunction.Sum(arg1:=1, arg1:=2, arg1:=3) 'Prints 1
End Sub

The behavior seems to be to use the first "version" of the named argument that is passed in the parameter list. IMHO this should _always_ be considered a coding error.

difficulty-02-ducky enhancement feature-inspections up-for-grabs

Most helpful comment

IRange and IWorksheetFunction are not marked with nonextensible and it looks like MSExcel typelib is full of such "extensible" interfaces (I bet on legacy support here being the culprit).

There is nothing special about IRange::Sort method in the typelib

        [helpcontext(0x00023348)]
        HRESULT _stdcall Sort(
                        [in, optional] VARIANT Key1, 
                        [in, optional, defaultvalue(1)] XlSortOrder Order1, 
                        [in, optional] VARIANT Key2, 
                        [in, optional] VARIANT Type, 
                        [in, optional, defaultvalue(1)] XlSortOrder Order2, 
                        [in, optional] VARIANT Key3, 
                        [in, optional, defaultvalue(1)] XlSortOrder Order3, 
                        [in, optional, defaultvalue(2)] XlYesNoGuess Header, 
                        [in, optional] VARIANT OrderCustom, 
                        [in, optional] VARIANT MatchCase, 
                        [in, optional, defaultvalue(2)] XlSortOrientation Orientation, 
                        [in, optional, defaultvalue(1)] XlSortMethod SortMethod, 
                        [in, optional, defaultvalue(0)] XlSortDataOption DataOption1, 
                        [in, optional, defaultvalue(0)] XlSortDataOption DataOption2, 
                        [in, optional, defaultvalue(0)] XlSortDataOption DataOption3, 
                        [out, retval] VARIANT* RHS);

It must be the "extensible" interface that allows repeated named params (and probably more funny stuff). And why not, when you have a custom impl of IDispatch it is actually doable. It's the IDispatch::GetIDsOfNames that maps param names to DISPIDs and some kind of Honey Badger implementation might not care about duplicates.

All 9 comments

Should we provide a quick-fix for this? The only reasonable quick-fix would be removing the later named arguments of the same name. Then again I'm not sure that's "good" behavior from a usability standpoint.

Additional question: Should the following spawn two instances of that inspection?

SomeFunction(arg1:=1, arg2:=2, arg1:=3, arg2:=4)

Note that this can serve as an edge-case for both detection and quick-fix (if it's implemented)

This would seem to only apply to the WorksheetFunction methods?

VBE seems to throw compile error Name already specified for all other members?

@ThunderFrame maybe it's something that can be inferred from the type library? ...I can't imagine the VBE special-casing a specific member of a specific library, it doesn't make sense...

I suspect it's the way VBE compiles certain types of member calls, based on the TLB definitions and attributes, just like the odd behavior with named arguments for InStr vs VBA.InStr.

I'm not saying there aren't _any_ other functions that have this same-named-argument problem, but there aren't any others I've found.

If we can identify the fault/attributes, we should be able to identify _all_ problematic members upon TLB read.

Range.Sort allows this also. I actually stumbled across this in an SO question:

ActiveSheet.Range("A:K").Sort _ 
 Key1:=ActiveSheet.Range("F2"), Order1:=xlAscending, Header:=xlYes, Ordercustom:=1, MatchCase:=False, _ 
 Orientation:=xlTopToBottom, DataOption1:=xlSortNormal, _ 
 Key2:=ActiveSheet.Range("E2"), Order2:=xlAscending, Header:=xlYes, Ordercustom:=1, MatchCase:=False, _ 
 Orientation:=xlTopToBottom, DataOption2:=xlSortNormal

This didn't fail until the OP added a _3rd copy_ of the repeated parameters.

It does appear to have something to do with how the compiler binds to functions specified by the TLB. Consider:

Dim foo As New Scripting.Dictionary
foo.Add Key:="foo", Item:="bar"
foo.Add Key:="foo", Key:="Foo", Item:="bar"  'Compile error - Name argument already specified.

Dim foo As Object
Set foo = CreateObject("Scripting.Dictionary")
foo.Add Key:="foo", Key:="Foo", Item:="bar"  'Runtime error - Wrong number of arguments.

The odd thing is that this implies that it is actually _late binding_ calls to Sort and WorksheetFunction from within Excel (see #2237).

@comintern seems the deeper we get into VBA's internals, the more everything I thought I knew gets shattered into thousands of little confused pieces, echoing "wtf!?" "wtf?" "wtf!!!" "wtf?!?!?!" as they fly around and splatter onto the wall.

IRange and IWorksheetFunction are not marked with nonextensible and it looks like MSExcel typelib is full of such "extensible" interfaces (I bet on legacy support here being the culprit).

There is nothing special about IRange::Sort method in the typelib

        [helpcontext(0x00023348)]
        HRESULT _stdcall Sort(
                        [in, optional] VARIANT Key1, 
                        [in, optional, defaultvalue(1)] XlSortOrder Order1, 
                        [in, optional] VARIANT Key2, 
                        [in, optional] VARIANT Type, 
                        [in, optional, defaultvalue(1)] XlSortOrder Order2, 
                        [in, optional] VARIANT Key3, 
                        [in, optional, defaultvalue(1)] XlSortOrder Order3, 
                        [in, optional, defaultvalue(2)] XlYesNoGuess Header, 
                        [in, optional] VARIANT OrderCustom, 
                        [in, optional] VARIANT MatchCase, 
                        [in, optional, defaultvalue(2)] XlSortOrientation Orientation, 
                        [in, optional, defaultvalue(1)] XlSortMethod SortMethod, 
                        [in, optional, defaultvalue(0)] XlSortDataOption DataOption1, 
                        [in, optional, defaultvalue(0)] XlSortDataOption DataOption2, 
                        [in, optional, defaultvalue(0)] XlSortDataOption DataOption3, 
                        [out, retval] VARIANT* RHS);

It must be the "extensible" interface that allows repeated named params (and probably more funny stuff). And why not, when you have a custom impl of IDispatch it is actually doable. It's the IDispatch::GetIDsOfNames that maps param names to DISPIDs and some kind of Honey Badger implementation might not care about duplicates.

@comintern The Range.Sort and Dictionary.Add methods were _only_ failing with 'Runtime error - Wrong number of arguments because you were providing _more_ arguments than the method accepts, regardless of whether they were named repetitively.

Range.Sort accepts up to 15 arguments. If you provide more than 15, named or otherwise, you get an error.

ActiveSheet.Range("A:K").Sort _
  key1:=ActiveSheet.Range("F2"), order1:=xlAscending, Header:=xlYes, ordercustom:=1, MatchCase:=False, _
  key2:=ActiveSheet.Range("E2"), order2:=xlAscending, Header:=xlYes, ordercustom:=1, MatchCase:=False, _
  key3:=ActiveSheet.Range("D2"), order3:=xlAscending, Header:=xlYes, ordercustom:=1, MatchCase:=False 'Works

ActiveSheet.Range("A:K").Sort _
  key1:=ActiveSheet.Range("F2"), order1:=xlAscending, Header:=xlYes, ordercustom:=1, MatchCase:=False, _
  key2:=ActiveSheet.Range("E2"), order2:=xlAscending, Header:=xlYes, ordercustom:=1, MatchCase:=False, _
  key3:=ActiveSheet.Range("D2"), order3:=xlAscending, Header:=xlYes, ordercustom:=1, MatchCase:=False, MatchCase:=False 'Runtime error - Wrong number of arguments


ActiveSheet.Range("A:K").Sort _
  MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, _
  MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, _
  MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False 'Sort Method of Range class failed

ActiveSheet.Range("A:K").Sort _
  MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, _
  MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, _
  MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False, MatchCase:=False 'Runtime error - Wrong number of arguments

But Dictionary.Add accepts 2 arguments, and yet you can provide more than 2 named arguments without getting Runtime error - Wrong number of arguments, but instead you'll get Name argument already specified

There must be something different about the signatures that makes the compiler treat them differently.

Was this page helpful?
0 / 5 - 0 ratings