sass 3.3.0.alpha.378 → 3.3.0.alpha.380

Sign up to get free protection for your applications and to get access to all the features.
data/REVISION CHANGED
@@ -1 +1 @@
1
- af904635d4da0719e029b715cd2acb9fa6446b0a
1
+ 898bec6bfb7fa4ce8a1ca8e2ff210387c7f98f0e
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.0.alpha.378
1
+ 3.3.0.alpha.380
@@ -1 +1 @@
1
- 11 October 2013 21:25:34 GMT
1
+ 11 October 2013 22:11:57 GMT
@@ -437,6 +437,7 @@ RUBY
437
437
 
438
438
  return [args, keywords] unless e
439
439
 
440
+ splat = nil
440
441
  loop do
441
442
  if @lexer.peek && @lexer.peek.type == :colon
442
443
  name = e
@@ -450,21 +451,19 @@ RUBY
450
451
 
451
452
  keywords[name.name] = value
452
453
  else
453
- unless keywords.empty?
454
+ if try_tok(:splat)
455
+ return args, keywords, splat, e if splat
456
+ splat, e = e, nil
457
+ elsif splat
458
+ raise SyntaxError.new("Only keyword arguments may follow variable arguments (...).")
459
+ elsif !keywords.empty?
454
460
  raise SyntaxError.new("Positional arguments must come before keyword arguments.")
455
461
  end
456
462
 
457
- if try_tok(:splat)
458
- splat = e
459
- return args, keywords, splat unless try_tok(:comma)
460
- kwarg_splat = assert_expr(subexpr, description)
461
- assert_tok(:splat)
462
- return args, keywords, splat, kwarg_splat
463
- end
464
- args << e
463
+ args << e if e
465
464
  end
466
465
 
467
- return args, keywords unless try_tok(:comma)
466
+ return args, keywords, splat unless try_tok(:comma)
468
467
  e = assert_expr(subexpr, description)
469
468
  end
470
469
  end
@@ -76,17 +76,18 @@ module Sass::Script::Tree
76
76
  sass
77
77
  end
78
78
 
79
- args = @args.map(&arg_to_sass).join(', ')
79
+ args = @args.map(&arg_to_sass)
80
80
  keywords = Sass::Util.hash_to_a(@keywords.as_stored).
81
- map {|k, v| "$#{dasherize(k, opts)}: #{arg_to_sass[v]}"}.join(', ')
81
+ map {|k, v| "$#{dasherize(k, opts)}: #{arg_to_sass[v]}"}
82
+
82
83
  # rubocop:disable RedundantSelf
83
84
  if self.splat
84
- splat = args.empty? && keywords.empty? ? "" : ", "
85
- splat = "#{splat}#{arg_to_sass[self.splat]}..."
86
- splat = "#{splat}, #{arg_to_sass[kwarg_splat]}..." if kwarg_splat
85
+ splat = "#{arg_to_sass[self.splat]}..."
86
+ kwarg_splat = "#{arg_to_sass[self.kwarg_splat]}..." if self.kwarg_splat
87
87
  end
88
88
  # rubocop:enable RedundantSelf
89
- arglist = "#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat}"
89
+
90
+ arglist = [args, splat, keywords, kwarg_splat].flatten.compact.join(', ')
90
91
  "#{dasherize(name, opts)}(#{arglist})"
91
92
  end
92
93
 
@@ -121,16 +122,16 @@ module Sass::Script::Tree
121
122
  def _perform(environment)
122
123
  args = Sass::Util.enum_with_index(@args).
123
124
  map {|a, i| perform_arg(a, environment, signature && signature.args[i])}
124
- splat = Sass::Tree::Visitors::Perform.perform_splat(@splat, @kwarg_splat, environment)
125
125
  keywords = Sass::Util.map_hash(@keywords) do |k, v|
126
126
  [k, perform_arg(v, environment, k.tr('-', '_'))]
127
127
  end
128
+ splat = Sass::Tree::Visitors::Perform.perform_splat(
129
+ @splat, keywords, @kwarg_splat, environment)
128
130
  if (fn = environment.function(@name))
129
- return perform_sass_fn(fn, args, keywords, splat, environment)
131
+ return perform_sass_fn(fn, args, splat, environment)
130
132
  end
131
133
 
132
- ruby_name = @name.tr('-', '_')
133
- args = construct_ruby_args(ruby_name, args, keywords, splat, environment)
134
+ args = construct_ruby_args(ruby_name, args, splat, environment)
134
135
 
135
136
  if Sass::Script::Functions.callable?(ruby_name)
136
137
  local_environment = Sass::Environment.new(environment.global_env, environment.options)
@@ -141,54 +142,7 @@ module Sass::Script::Tree
141
142
  opts(to_literal(args))
142
143
  end
143
144
  rescue ArgumentError => e
144
- message = e.message
145
-
146
- # If this is a legitimate Ruby-raised argument error, re-raise it.
147
- # Otherwise, it's an error in the user's stylesheet, so wrap it.
148
- if Sass::Util.rbx?
149
- # Rubinius has a different error report string than vanilla Ruby. It
150
- # also doesn't put the actual method for which the argument error was
151
- # thrown in the backtrace, nor does it include `send`, so we look for
152
- # `_perform`.
153
- if e.message =~ /^method '([^']+)': given (\d+), expected (\d+)/
154
- error_name, given, expected = $1, $2, $3
155
- raise e if error_name != ruby_name || e.backtrace[0] !~ /:in `_perform'$/
156
- message = "wrong number of arguments (#{given} for #{expected})"
157
- end
158
- elsif Sass::Util.jruby?
159
- if Sass::Util.jruby1_6?
160
- should_maybe_raise = e.message =~ /^wrong number of arguments \((\d+) for (\d+)\)/ &&
161
- # The one case where JRuby does include the Ruby name of the function
162
- # is manually-thrown ArgumentErrors, which are indistinguishable from
163
- # legitimate ArgumentErrors. We treat both of these as
164
- # Sass::SyntaxErrors even though it can hide Ruby errors.
165
- e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
166
- else
167
- should_maybe_raise =
168
- e.message =~ /^wrong number of arguments calling `[^`]+` \((\d+) for (\d+)\)/
169
- given, expected = $1, $2
170
- end
171
-
172
- if should_maybe_raise
173
- # JRuby 1.7 includes __send__ before send and _perform.
174
- trace = e.backtrace.dup
175
- raise e if !Sass::Util.jruby1_6? && trace.shift !~ /:in `__send__'$/
176
-
177
- # JRuby (as of 1.7.2) doesn't put the actual method
178
- # for which the argument error was thrown in the backtrace, so we
179
- # detect whether our send threw an argument error.
180
- if !(trace[0] =~ /:in `send'$/ && trace[1] =~ /:in `_perform'$/)
181
- raise e
182
- elsif !Sass::Util.jruby1_6?
183
- # JRuby 1.7 doesn't use standard formatting for its ArgumentErrors.
184
- message = "wrong number of arguments (#{given} for #{expected})"
185
- end
186
- end
187
- elsif e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
188
- e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
189
- raise e
190
- end
191
- raise Sass::SyntaxError.new("#{message} for `#{name}'")
145
+ reformat_argument_error(e)
192
146
  end
193
147
 
194
148
  # Compass historically overrode this before it changed name to {Funcall#to_value}.
@@ -206,6 +160,10 @@ module Sass::Script::Tree
206
160
 
207
161
  private
208
162
 
163
+ def ruby_name
164
+ @ruby_name ||= @name.tr('-', '_')
165
+ end
166
+
209
167
  def perform_arg(argument, environment, name)
210
168
  return argument if signature && signature.delayed_args.include?(name)
211
169
  argument.perform(environment)
@@ -215,15 +173,14 @@ module Sass::Script::Tree
215
173
  @signature ||= Sass::Script::Functions.signature(name.to_sym, @args.size, @keywords.size)
216
174
  end
217
175
 
218
- def construct_ruby_args(name, args, keywords, splat, environment)
176
+ def construct_ruby_args(name, args, splat, environment)
219
177
  args += splat.to_a if splat
220
178
 
221
- # If variable arguments were passed, there won't be any explicit keywords.
222
- if splat && !splat.keywords.empty?
223
- old_keywords_accessed = splat.keywords_accessed
224
- keywords = splat.keywords
225
- splat.keywords_accessed = old_keywords_accessed
226
- end
179
+ # All keywords are contained in splat.keywords for consistency,
180
+ # even if there were no splats passed in.
181
+ old_keywords_accessed = splat.keywords_accessed
182
+ keywords = splat.keywords
183
+ splat.keywords_accessed = old_keywords_accessed
227
184
 
228
185
  unless (signature = Sass::Script::Functions.signature(name.to_sym, args.size, keywords.size))
229
186
  return args if keywords.empty?
@@ -267,8 +224,8 @@ module Sass::Script::Tree
267
224
  args
268
225
  end
269
226
 
270
- def perform_sass_fn(function, args, keywords, splat, environment)
271
- Sass::Tree::Visitors::Perform.perform_arguments(function, args, keywords, splat) do |env|
227
+ def perform_sass_fn(function, args, splat, environment)
228
+ Sass::Tree::Visitors::Perform.perform_arguments(function, args, splat) do |env|
272
229
  env.caller = Sass::Environment.new(environment)
273
230
 
274
231
  val = catch :_sass_return do
@@ -278,5 +235,56 @@ module Sass::Script::Tree
278
235
  val
279
236
  end
280
237
  end
238
+
239
+ def reformat_argument_error(e)
240
+ message = e.message
241
+
242
+ # If this is a legitimate Ruby-raised argument error, re-raise it.
243
+ # Otherwise, it's an error in the user's stylesheet, so wrap it.
244
+ if Sass::Util.rbx?
245
+ # Rubinius has a different error report string than vanilla Ruby. It
246
+ # also doesn't put the actual method for which the argument error was
247
+ # thrown in the backtrace, nor does it include `send`, so we look for
248
+ # `_perform`.
249
+ if e.message =~ /^method '([^']+)': given (\d+), expected (\d+)/
250
+ error_name, given, expected = $1, $2, $3
251
+ raise e if error_name != ruby_name || e.backtrace[0] !~ /:in `_perform'$/
252
+ message = "wrong number of arguments (#{given} for #{expected})"
253
+ end
254
+ elsif Sass::Util.jruby?
255
+ if Sass::Util.jruby1_6?
256
+ should_maybe_raise = e.message =~ /^wrong number of arguments \((\d+) for (\d+)\)/ &&
257
+ # The one case where JRuby does include the Ruby name of the function
258
+ # is manually-thrown ArgumentErrors, which are indistinguishable from
259
+ # legitimate ArgumentErrors. We treat both of these as
260
+ # Sass::SyntaxErrors even though it can hide Ruby errors.
261
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
262
+ else
263
+ should_maybe_raise =
264
+ e.message =~ /^wrong number of arguments calling `[^`]+` \((\d+) for (\d+)\)/
265
+ given, expected = $1, $2
266
+ end
267
+
268
+ if should_maybe_raise
269
+ # JRuby 1.7 includes __send__ before send and _perform.
270
+ trace = e.backtrace.dup
271
+ raise e if !Sass::Util.jruby1_6? && trace.shift !~ /:in `__send__'$/
272
+
273
+ # JRuby (as of 1.7.2) doesn't put the actual method
274
+ # for which the argument error was thrown in the backtrace, so we
275
+ # detect whether our send threw an argument error.
276
+ if !(trace[0] =~ /:in `send'$/ && trace[1] =~ /:in `_perform'$/)
277
+ raise e
278
+ elsif !Sass::Util.jruby1_6?
279
+ # JRuby 1.7 doesn't use standard formatting for its ArgumentErrors.
280
+ message = "wrong number of arguments (#{given} for #{expected})"
281
+ end
282
+ end
283
+ elsif e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
284
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
285
+ raise e
286
+ end
287
+ raise Sass::SyntaxError.new("#{message} for `#{name}'")
288
+ end
281
289
  end
282
290
  end
@@ -206,15 +206,16 @@ class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
206
206
  end
207
207
 
208
208
  unless node.args.empty? && node.keywords.empty? && node.splat.nil?
209
- args = node.args.map(&arg_to_sass).join(", ")
209
+ args = node.args.map(&arg_to_sass)
210
210
  keywords = Sass::Util.hash_to_a(node.keywords).
211
- map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}.join(', ')
211
+ map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}
212
+
212
213
  if node.splat
213
- splat = args.empty? && keywords.empty? ? "" : ", "
214
- splat = "#{splat}#{arg_to_sass[node.splat]}..."
215
- splat = "#{splat}, #{node.kwarg_splat.inspect}..." if node.kwarg_splat
214
+ splat = "#{arg_to_sass[node.splat]}..."
215
+ kwarg_splat = "#{arg_to_sass[node.kwarg_splat]}..." if node.kwarg_splat
216
216
  end
217
- arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
217
+
218
+ arglist = "(#{[args, splat, keywords, kwarg_splat].flatten.compact.join(', ')})"
218
219
  end
219
220
  "#{tab_str}#{@format == :sass ? '+' : '@include '}" +
220
221
  "#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
@@ -11,16 +11,15 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
11
11
  # @api private
12
12
  # @comment
13
13
  # rubocop:disable MethodLength
14
- def perform_arguments(callable, args, keywords, splat)
14
+ def perform_arguments(callable, args, splat)
15
15
  desc = "#{callable.type.capitalize} #{callable.name}"
16
16
  downcase_desc = "#{callable.type} #{callable.name}"
17
17
 
18
- # If variable arguments were passed, there won't be any explicit keywords.
19
- if splat && !splat.keywords.empty?
20
- old_keywords_accessed = splat.keywords_accessed
21
- keywords = splat.keywords
22
- splat.keywords_accessed = old_keywords_accessed
23
- end
18
+ # All keywords are contained in splat.keywords for consistency,
19
+ # even if there were no splats passed in.
20
+ old_keywords_accessed = splat.keywords_accessed
21
+ keywords = splat.keywords
22
+ splat.keywords_accessed = old_keywords_accessed
24
23
 
25
24
  begin
26
25
  unless keywords.empty?
@@ -99,33 +98,34 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
99
98
 
100
99
  # @api private
101
100
  # @return [Sass::Script::Value::ArgList]
102
- def perform_splat(splat, kwarg_splat, environment)
103
- return unless splat
104
- splat = splat.perform(environment)
105
- unless kwarg_splat
106
- return splat if splat.is_a?(Sass::Script::Value::ArgList)
107
- if splat.is_a?(Sass::Script::Value::Map)
108
- args = []
101
+ def perform_splat(splat, performed_keywords, kwarg_splat, environment)
102
+ args, kwargs, separator = [], Sass::Util.ordered_hash, :comma
103
+
104
+ if splat
105
+ splat = splat.perform(environment)
106
+ separator = splat.separator || separator
107
+ if splat.is_a?(Sass::Script::Value::ArgList)
108
+ args = splat.to_a
109
+ kwargs = splat.keywords
110
+ elsif splat.is_a?(Sass::Script::Value::Map)
109
111
  kwargs = arg_hash(splat)
110
112
  else
111
113
  args = splat.to_a
112
- kwargs = {}
113
114
  end
114
- return Sass::Script::Value::ArgList.new(args, kwargs, splat.separator || :comma)
115
115
  end
116
116
 
117
- kwarg_splat = kwarg_splat.perform(environment)
118
- unless kwarg_splat.is_a?(Sass::Script::Value::Map)
119
- raise Sass::SyntaxError.new("Variable keyword arguments must be a map " +
120
- "(was #{kwarg_splat.inspect}).")
121
- end
117
+ kwargs = kwargs.merge(performed_keywords)
122
118
 
123
- if splat.is_a?(Sass::Script::Value::ArgList)
124
- return Sass::Script::Value::ArgList.new(
125
- splat.value, splat.keywords.merge(arg_hash(kwarg_splat)), splat.separator)
126
- else
127
- return Sass::Script::Value::ArgList.new(splat.to_a, arg_hash(kwarg_splat), splat.separator)
119
+ if kwarg_splat
120
+ kwarg_splat = kwarg_splat.perform(environment)
121
+ unless kwarg_splat.is_a?(Sass::Script::Value::Map)
122
+ raise Sass::SyntaxError.new("Variable keyword arguments must be a map " +
123
+ "(was #{kwarg_splat.inspect}).")
124
+ end
125
+ kwargs = kwargs.merge(arg_hash(kwarg_splat))
128
126
  end
127
+
128
+ Sass::Script::Value::ArgList.new(args, kwargs, separator)
129
129
  end
130
130
 
131
131
  private
@@ -326,9 +326,9 @@ class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
326
326
 
327
327
  args = node.args.map {|a| a.perform(@environment)}
328
328
  keywords = Sass::Util.map_hash(node.keywords) {|k, v| [k, v.perform(@environment)]}
329
- splat = self.class.perform_splat(node.splat, node.kwarg_splat, @environment)
329
+ splat = self.class.perform_splat(node.splat, keywords, node.kwarg_splat, @environment)
330
330
 
331
- self.class.perform_arguments(mixin, args, keywords, splat) do |env|
331
+ self.class.perform_arguments(mixin, args, splat) do |env|
332
332
  env.caller = Sass::Environment.new(@environment)
333
333
  env.content = [node.children, @environment] if node.has_children
334
334
 
@@ -1629,6 +1629,7 @@ SCSS
1629
1629
 
1630
1630
  .foo
1631
1631
  +foo($list..., $map...)
1632
+ +foo(pos, $list..., $kwd: val, $map...)
1632
1633
  SASS
1633
1634
  @mixin foo($a: b, $c: d) {
1634
1635
  a: $a;
@@ -1637,6 +1638,7 @@ SASS
1637
1638
 
1638
1639
  .foo {
1639
1640
  @include foo($list..., $map...);
1641
+ @include foo(pos, $list..., $kwd: val, $map...);
1640
1642
  }
1641
1643
  SCSS
1642
1644
  end
@@ -1675,6 +1677,7 @@ SCSS
1675
1677
 
1676
1678
  .foo
1677
1679
  a: foo($list..., $map...)
1680
+ b: foo(pos, $list..., $kwd: val, $map...)
1678
1681
  SASS
1679
1682
  @function foo($a: b, $c: d) {
1680
1683
  @return foo;
@@ -1682,6 +1685,7 @@ SASS
1682
1685
 
1683
1686
  .foo {
1684
1687
  a: foo($list..., $map...);
1688
+ b: foo(pos, $list..., $kwd: val, $map...);
1685
1689
  }
1686
1690
  SCSS
1687
1691
  end
@@ -1074,6 +1074,108 @@ CSS
1074
1074
  SCSS
1075
1075
  end
1076
1076
 
1077
+ def test_mixin_splat_after_keyword_args
1078
+ assert_equal <<CSS, render(<<SCSS)
1079
+ .foo {
1080
+ a: 1;
1081
+ b: 2;
1082
+ c: 3; }
1083
+ CSS
1084
+ @mixin foo($a, $b, $c) {
1085
+ a: 1;
1086
+ b: 2;
1087
+ c: 3;
1088
+ }
1089
+
1090
+ .foo {
1091
+ @include foo(1, $c: 3, 2...);
1092
+ }
1093
+ SCSS
1094
+ end
1095
+
1096
+ def test_mixin_keyword_args_after_splat
1097
+ assert_equal <<CSS, render(<<SCSS)
1098
+ .foo {
1099
+ a: 1;
1100
+ b: 2;
1101
+ c: 3; }
1102
+ CSS
1103
+ @mixin foo($a, $b, $c) {
1104
+ a: 1;
1105
+ b: 2;
1106
+ c: 3;
1107
+ }
1108
+
1109
+ .foo {
1110
+ @include foo(1, 2..., $c: 3);
1111
+ }
1112
+ SCSS
1113
+ end
1114
+
1115
+ def test_mixin_keyword_splat_after_keyword_args
1116
+ assert_equal <<CSS, render(<<SCSS)
1117
+ .foo {
1118
+ a: 1;
1119
+ b: 2;
1120
+ c: 3; }
1121
+ CSS
1122
+ @mixin foo($a, $b, $c) {
1123
+ a: 1;
1124
+ b: 2;
1125
+ c: 3;
1126
+ }
1127
+
1128
+ .foo {
1129
+ @include foo(1, $b: 2, (c: 3)...);
1130
+ }
1131
+ SCSS
1132
+ end
1133
+
1134
+ def test_mixin_triple_keyword_splat_merge
1135
+ assert_equal <<CSS, render(<<SCSS)
1136
+ .foo {
1137
+ foo: 1;
1138
+ bar: 2;
1139
+ kwarg: 3;
1140
+ a: 3;
1141
+ b: 2;
1142
+ c: 3; }
1143
+ CSS
1144
+ @mixin foo($foo, $bar, $kwarg, $a, $b, $c) {
1145
+ foo: $foo;
1146
+ bar: $bar;
1147
+ kwarg: $kwarg;
1148
+ a: $a;
1149
+ b: $b;
1150
+ c: $c;
1151
+ }
1152
+
1153
+ @mixin bar($args...) {
1154
+ @include foo($args..., $bar: 2, $a: 2, $b: 2, (kwarg: 3, a: 3, c: 3)...);
1155
+ }
1156
+
1157
+ .foo {
1158
+ @include bar($foo: 1, $a: 1, $b: 1, $c: 1);
1159
+ }
1160
+ SCSS
1161
+ end
1162
+
1163
+ def test_mixin_conflicting_splat_after_keyword_args
1164
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
1165
+ Mixin foo was passed argument $b both by position and by name.
1166
+ MESSAGE
1167
+ @mixin foo($a, $b, $c) {
1168
+ a: 1;
1169
+ b: 2;
1170
+ c: 3;
1171
+ }
1172
+
1173
+ .foo {
1174
+ @include foo(1, $b: 2, 3...);
1175
+ }
1176
+ SCSS
1177
+ end
1178
+
1077
1179
  def test_mixin_keyword_splat_must_have_string_keys
1078
1180
  assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render <<SCSS}
1079
1181
  Variable keyword argument map must have string keys.
@@ -1087,6 +1189,22 @@ MESSAGE
1087
1189
  SCSS
1088
1190
  end
1089
1191
 
1192
+ def test_mixin_positional_arg_after_splat
1193
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
1194
+ Only keyword arguments may follow variable arguments (...).
1195
+ MESSAGE
1196
+ @mixin foo($a, $b, $c) {
1197
+ a: 1;
1198
+ b: 2;
1199
+ c: 3;
1200
+ }
1201
+
1202
+ .foo {
1203
+ @include foo(1, 2..., 3);
1204
+ }
1205
+ SCSS
1206
+ end
1207
+
1090
1208
  def test_mixin_var_args_with_keyword
1091
1209
  assert_raise_message(Sass::SyntaxError, "Positional arguments must come before keyword arguments.") {render <<SCSS}
1092
1210
  @mixin foo($a, $b...) {
@@ -1397,6 +1515,98 @@ CSS
1397
1515
  SCSS
1398
1516
  end
1399
1517
 
1518
+ def test_function_splat_after_keyword_args
1519
+ assert_equal <<CSS, render(<<SCSS)
1520
+ .foo {
1521
+ val: "a: 1, b: 2, c: 3"; }
1522
+ CSS
1523
+ @function foo($a, $b, $c) {
1524
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1525
+ }
1526
+
1527
+ .foo {
1528
+ val: foo(1, $c: 3, 2...);
1529
+ }
1530
+ SCSS
1531
+ end
1532
+
1533
+ def test_function_keyword_args_after_splat
1534
+ assert_equal <<CSS, render(<<SCSS)
1535
+ .foo {
1536
+ val: "a: 1, b: 2, c: 3"; }
1537
+ CSS
1538
+ @function foo($a, $b, $c) {
1539
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1540
+ }
1541
+
1542
+ .foo {
1543
+ val: foo(1, 2..., $c: 3);
1544
+ }
1545
+ SCSS
1546
+ end
1547
+
1548
+ def test_function_keyword_splat_after_keyword_args
1549
+ assert_equal <<CSS, render(<<SCSS)
1550
+ .foo {
1551
+ val: "a: 1, b: 2, c: 3"; }
1552
+ CSS
1553
+ @function foo($a, $b, $c) {
1554
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1555
+ }
1556
+
1557
+ .foo {
1558
+ val: foo(1, $b: 2, (c: 3)...);
1559
+ }
1560
+ SCSS
1561
+ end
1562
+
1563
+ def test_function_triple_keyword_splat_merge
1564
+ assert_equal <<CSS, render(<<SCSS)
1565
+ .foo {
1566
+ val: "foo: 1, bar: 2, kwarg: 3, a: 3, b: 2, c: 3"; }
1567
+ CSS
1568
+ @function foo($foo, $bar, $kwarg, $a, $b, $c) {
1569
+ @return "foo: \#{$foo}, bar: \#{$bar}, kwarg: \#{$kwarg}, a: \#{$a}, b: \#{$b}, c: \#{$c}";
1570
+ }
1571
+
1572
+ @function bar($args...) {
1573
+ @return foo($args..., $bar: 2, $a: 2, $b: 2, (kwarg: 3, a: 3, c: 3)...);
1574
+ }
1575
+
1576
+ .foo {
1577
+ val: bar($foo: 1, $a: 1, $b: 1, $c: 1);
1578
+ }
1579
+ SCSS
1580
+ end
1581
+
1582
+ def test_function_conflicting_splat_after_keyword_args
1583
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
1584
+ Function foo was passed argument $b both by position and by name.
1585
+ MESSAGE
1586
+ @function foo($a, $b, $c) {
1587
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1588
+ }
1589
+
1590
+ .foo {
1591
+ val: foo(1, $b: 2, 3...);
1592
+ }
1593
+ SCSS
1594
+ end
1595
+
1596
+ def test_function_positional_arg_after_splat
1597
+ assert_raise_message(Sass::SyntaxError, <<MESSAGE.rstrip) {render(<<SCSS)}
1598
+ Only keyword arguments may follow variable arguments (...).
1599
+ MESSAGE
1600
+ @function foo($a, $b, $c) {
1601
+ @return "a: \#{$a}, b: \#{$b}, c: \#{$c}";
1602
+ }
1603
+
1604
+ .foo {
1605
+ val: foo(1, 2..., 3);
1606
+ }
1607
+ SCSS
1608
+ end
1609
+
1400
1610
  def test_function_var_args_with_keyword
1401
1611
  assert_raise_message(Sass::SyntaxError, "Positional arguments must come before keyword arguments.") {render <<SCSS}
1402
1612
  @function foo($a, $b...) {
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sass
3
3
  version: !ruby/object:Gem::Version
4
- hash: 592302585
4
+ hash: 592302581
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 3
8
8
  - 3
9
9
  - 0
10
10
  - alpha
11
- - 378
12
- version: 3.3.0.alpha.378
11
+ - 380
12
+ version: 3.3.0.alpha.380
13
13
  platform: ruby
14
14
  authors:
15
15
  - Nathan Weizenbaum