[PUP-4398] Splat unfolding not supported in method call syntax Created: 2015/04/11  Updated: 2015/05/20  Resolved: 2015/05/20

Status: Closed
Project: Puppet
Component/s: Language
Affects Version/s: PUP 4.0.0
Fix Version/s: PUP 3.8.1, PUP 4.1.0

Type: Bug Priority: Normal
Reporter: Peter Huene Assignee: Unassigned
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Relates
Template:
Epic Link: 4.x Language
Story Points: 1
Sprint: Language 2015-04-29
Release Notes: Bug Fix

 Description   

I expected the following to be equivalent:

Function call sytnax:

with(*[1, 2, 3]) |$x, $y, $z| {
    notice $x, $y, $z
}

Method call syntax:

(*[1, 2, 3]).with |$x, $y, $z| {
    notice $x, $y, $z
}

The function call outputs:

Notice: Scope(Class[main]): 1 2 3

The method call outputs an error:

Error: Evaluation Error: Parameter $y is required but no value was given at...

In the latter, $x is the array without being unfolded.

Note: I could not find a workaround in the syntax to be able to splat for a method call.

risk: medium
probability: low (unfolding IN arrays/method calls/etc)
severity: medium (no work arounds other than using different class function structure)
test layer: unit



 Comments   
Comment by Henrik Lindberg [ 2015/04/14 ]

Added puppet 3.8.1 as a fix as this should be made available to future parser on 3.x.

Comment by Henrik Lindberg [ 2015/04/15 ]

The problem was caused by the unfold logic not recognizing a "splat" operator inside parentheses. This was not unique to method call, but also, if splatting inside an array, a case or selector option. The fix for this applies to all locations.

Comment by Thomas Hallgren [ 2015/04/19 ]

Merged to 3.x at 039d4ed

Comment by Thomas Hallgren [ 2015/04/20 ]

Merged to master at b2637ac

Comment by Eric Thompson [ 2015/04/21 ]

validated on ubuntu14 at SHA: ffaffb9

[root@plgf5s85ojlj8m1 puppet]# cat method_splat.pp
(*[1, 2, 3]).with |$x, $y, $z| {
    notice $x, $y, $z
}
[root@plgf5s85ojlj8m1 puppet]# puppet apply method_splat.pp
Notice: Scope(Class[main]): 1 2 3
Notice: Compiled catalog for plgf5s85ojlj8m1.delivery.puppetlabs.net in environment production in 0.37 seconds
Notice: Applied catalog in 0.02 seconds

Comment by Eric Thompson [ 2015/04/21 ]

validated on windows2012r2-rubyx64 at SHA: 39bd7a7

Administrator@f67onhgjleryc6t /opt/puppet-git-repos/puppet
$ cmd /c puppet apply method_splat.pp
Notice: Scope(Class[main]): 1 2 3
Notice: Compiled catalog for f67onhgjleryc6t.delivery.puppetlabs.net in environment production in 0.42 seconds
Notice: Applied catalog in 0.03 seconds

Comment by Nicholas Fagerlund [ 2015/05/14 ]

Docs needs more info on this ticket.

Do we consider this a good, practical usage, or a fringe thing that just happens to work? The issue is cloudy because:

  • I'm pretty sure the spec said chain-style calls come after the first argument, followed by additional arguments. This uses multiple arguments as the first argument.
  • It looks like something like (1, 2, 3).with ... should be equivalent to the above usages, but it's not allowed. (Results in Error: Could not parse for environment production: Syntax error at ',') Having the splat version be special is inconsistent and hard to explain.
  • If you put parentheses around the first argument of a chain-style call, and you just happen to have multiple chain-style calls in a row, they'll eat each others incidental return values. Like this:

(*[1, 2, 3]).notice
(*[4, 5, 6]).notice
(*[4, 5, 6]).notice

Notice: Scope(Class[main]): 1 2 3 4 5 6
Notice: Scope(Class[main]): 1 2 3 4 5 6 4 5 6
Notice: Scope(Class[main]): 1 2 3 4 5 6 4 5 6

This seems to me like something that shouldn't be documented, unless we get it working consistently with unsplatted comma-lists. And even then, the squirreliness in my example here imposes extra explanatory burden.

Comment by Henrik Lindberg [ 2015/05/14 ]

The surprising effect observed is because if there are parentheses there, they are assumed to defined additional arguments - the example results in an expression equal to that below (where explicit parens have been inserted to show what the parser thinks this means):

(((*[1, 2, 3]).notice(*[4, 5, 6])).notice(*[4, 5, 6])).notice

That effect has nothing to do with splatting, nor "eating the return value", nor "first chain call uses parentheses". This has the same "problem":

1.notice
(2).notice
(3).notice

Rewriting the first example makes it work:

(*[1, 2, 3]).notice()
(*[4, 5, 6]).notice()
(*[4, 5, 6]).notice()

From a documentation standpoint, it is worth pointing out the difference between:

1.notice() (2).notice
1.notice (2).notice

i.e. "if you have no additional arguments, you must use () to indicate that there are no additional arguments if your next expression is a parenthesized expression. Alternatively you can separate the expressions using a semicolon".

1.notice; (2).notice

As a human it is easy to assign meaning to whitespace - the parser is seeing something like this:

1 . notice ( 2 ) . notice

It is now easier to spot the ambiguity that the parser resolves by giving "arguments in parentheses" higher precedence.

Generated at Mon Aug 19 09:54:01 PDT 2019 using JIRA 7.7.1#77002-sha1:e75ca93d5574d9409c0630b81c894d9065296414.