From d5e8bd82c8ec489a7850ab52964d7ff97d009289 Mon Sep 17 00:00:00 2001 From: Marcio Cruz de Almeida <67694075+marciocadev@users.noreply.github.com> Date: Wed, 2 Aug 2023 17:55:19 -0300 Subject: [PATCH] feat(compiler): variadic arguments (#3643) This PR adds the possibility of using variadic arguments Unfortunately, I had to add the `Clone` attribute to many `enum` and `struct` to be able to create a list of arguments compatible with the requirements for verifying the typing of the variadic argument. I needed to implement `PartialEq` for `Type`, `TypeRef`, `Class`, `Interface`, `Struct`, and `Enum`. I'm not sure if it was done in the best way, but it fulfills this specific case. - [x] Validates if the last argument (variadic) of the function accepts multiple elements. - [x] Validates if the only argument (variadic) of the function accepts multiple elements. - [x] Validates if all types of the variadic argument are the same. - [ ] Accept range as a variadic argument. Closes #125 Closes #397 ## Checklist - [x] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted) - [x] Description explains motivation and solution - [x] Tests added (always) - [ ] Docs updated (only required for features) - [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing *By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*. --- .../invalid/function_variadic_definition.w | 25 +++ .../tests/valid/function_variadic_arguments.w | 30 ++++ libs/tree-sitter-wing/grammar.js | 3 + .../test/corpus/statements/statements.txt | 51 ++++++ libs/wingc/src/ast.rs | 1 + libs/wingc/src/fold.rs | 1 + libs/wingc/src/jsify.rs | 6 +- libs/wingc/src/lib.rs | 4 + libs/wingc/src/parser.rs | 13 +- libs/wingc/src/type_check.rs | 82 ++++++++- libs/wingc/src/type_check/jsii_importer.rs | 2 + tools/hangar/__snapshots__/invalid.ts.snap | 57 ++++++ ...ion_variadic_arguments.w_compile_tf-aws.md | 164 ++++++++++++++++++ .../function_variadic_arguments.w_test_sim.md | 12 ++ 14 files changed, 438 insertions(+), 13 deletions(-) create mode 100644 examples/tests/invalid/function_variadic_definition.w create mode 100644 examples/tests/valid/function_variadic_arguments.w create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/function_variadic_arguments.w_compile_tf-aws.md create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/function_variadic_arguments.w_test_sim.md diff --git a/examples/tests/invalid/function_variadic_definition.w b/examples/tests/invalid/function_variadic_definition.w new file mode 100644 index 00000000000..6358d470a5a --- /dev/null +++ b/examples/tests/invalid/function_variadic_definition.w @@ -0,0 +1,25 @@ +let f1 = (...args: Array, x:num) => {}; +// ^^^^ Variadic parameters must always be the last parameter in a function. + +let f2 = (...nums: Array, ...strs: Array) => {}; +// ^^^^ Variadic parameters must always be the last parameter in a function. + +let f3 = (...args: Array) => {}; +f3(1, true, 2); +// ^^^^ Expected type to be num, but got bool instead. + +let f4 = (...args: Set) => {}; +// ^^^^ Variadic parameters must be type Array or MutArray. + +let f5 = (...args: Array) => {}; +f5(args: 1, 2); +// ^^^^^^^^^^^^^^ No named arguments expected + +bring cloud; +let bucket1 = new cloud.Bucket() as "bucket1"; +let bucket2 = new cloud.Bucket() as "bucket2"; +let funcBucket = (...buckets: Array) => { + assert(buckets.length == 2); +}; +funcBucket(bucket1, true, bucket2); +// ^^^^ Expected type to be Bucket, but got bool instead. diff --git a/examples/tests/valid/function_variadic_arguments.w b/examples/tests/valid/function_variadic_arguments.w new file mode 100644 index 00000000000..3ae3eb4bbc0 --- /dev/null +++ b/examples/tests/valid/function_variadic_arguments.w @@ -0,0 +1,30 @@ +bring cloud; +let bucket1 = new cloud.Bucket() as "bucket1"; +let bucket2 = new cloud.Bucket() as "bucket2"; +let funcBucket = (...buckets: Array) => { + assert(buckets.length == 2); +}; +funcBucket(bucket1, bucket2); + +let func1 = (var x: num, y: str?, var ...args: MutArray) => { + assert(x == 1); + assert(y == "something" || y == nil); + assert(args.length == 4); + for i in args { + assert(i > 0 && i < 5); + } + args.push(10); + assert(args.at(4) == 10); +}; +func1(1, "something", 1, 2, 3, 4); +func1(1, nil, 1, 2, 3, 4); + +let addNums = (...nums: MutArray):num => { + let var total = 0; + for n in nums { + total = total + n; + } + return total; +}; +assert(addNums(1, 2, 3) == 6); +assert(addNums() == 0); diff --git a/libs/tree-sitter-wing/grammar.js b/libs/tree-sitter-wing/grammar.js index cea7ad7174b..be007c672b0 100644 --- a/libs/tree-sitter-wing/grammar.js +++ b/libs/tree-sitter-wing/grammar.js @@ -511,9 +511,12 @@ module.exports = grammar({ access_modifier: ($) => choice("public", "private", "protected"), + variadic: ($) => "...", + parameter_definition: ($) => seq( optional(field("reassignable", $.reassignable)), + optional(field("variadic", $.variadic)), field("name", $.identifier), $._type_annotation ), diff --git a/libs/tree-sitter-wing/test/corpus/statements/statements.txt b/libs/tree-sitter-wing/test/corpus/statements/statements.txt index 82be7d997b2..6d8f23e0e40 100644 --- a/libs/tree-sitter-wing/test/corpus/statements/statements.txt +++ b/libs/tree-sitter-wing/test/corpus/statements/statements.txt @@ -214,6 +214,57 @@ inflight (callback: (num,num):bool) => {}; return_type: (builtin_type)))) block: (block)))) +================================================================================ +Inflight closure with variadic argument +================================================================================ + +inflight (var ...z: MutArray):bool => {}; + +-------------------------------------------------------------------------------- + +(source + (expression_statement + (inflight_closure + (inflight_specifier) + parameter_list: (parameter_list + (parameter_definition + reassignable: (reassignable) + variadic: (variadic) + name: (identifier) + type: (mutable_container_type + type_parameter: (builtin_type)))) + type: (builtin_type) + block: (block)))) + +================================================================================ +Inflight closure with parameter and variadic argument +================================================================================ + +inflight (var x: num, y: Array, ...z: Array):bool => {}; + +-------------------------------------------------------------------------------- + +(source + (expression_statement + (inflight_closure + (inflight_specifier) + parameter_list: (parameter_list + (parameter_definition + reassignable: (reassignable) + name: (identifier) + type: (builtin_type)) + (parameter_definition + name: (identifier) + type: (immutable_container_type + type_parameter: (builtin_type))) + (parameter_definition + variadic: (variadic) + name: (identifier) + type: (immutable_container_type + type_parameter: (builtin_type)))) + type: (builtin_type) + block: (block)))) + ================================================================================ Struct definition ================================================================================ diff --git a/libs/wingc/src/ast.rs b/libs/wingc/src/ast.rs index fa0433da7ab..6a8b6ee8c94 100644 --- a/libs/wingc/src/ast.rs +++ b/libs/wingc/src/ast.rs @@ -269,6 +269,7 @@ pub struct FunctionParameter { pub name: Symbol, pub type_annotation: TypeAnnotation, pub reassignable: bool, + pub variadic: bool, } #[derive(Debug)] diff --git a/libs/wingc/src/fold.rs b/libs/wingc/src/fold.rs index 3dd85ca33a6..e47fbd04cdc 100644 --- a/libs/wingc/src/fold.rs +++ b/libs/wingc/src/fold.rs @@ -423,6 +423,7 @@ where name: f.fold_symbol(node.name), type_annotation: f.fold_type_annotation(node.type_annotation), reassignable: node.reassignable, + variadic: node.variadic, } } diff --git a/libs/wingc/src/jsify.rs b/libs/wingc/src/jsify.rs index 5af9e7f10b6..33c79c41d7c 100644 --- a/libs/wingc/src/jsify.rs +++ b/libs/wingc/src/jsify.rs @@ -831,7 +831,11 @@ impl<'a> JSifier<'a> { let mut parameter_list = vec![]; for p in &func_def.signature.parameters { - parameter_list.push(p.name.to_string()); + if p.variadic { + parameter_list.push("...".to_string() + &p.name.to_string()); + } else { + parameter_list.push(p.name.to_string()); + } } let (name, arrow) = match &func_def.name { diff --git a/libs/wingc/src/lib.rs b/libs/wingc/src/lib.rs index 06ad7a8d9a9..1725d3f643c 100644 --- a/libs/wingc/src/lib.rs +++ b/libs/wingc/src/lib.rs @@ -215,6 +215,7 @@ pub fn type_check( name: "message".into(), typeref: types.string(), docs: Docs::with_summary("The message to log"), + variadic: false, }], return_type: types.void(), phase: Phase::Independent, @@ -232,6 +233,7 @@ pub fn type_check( name: "condition".into(), typeref: types.bool(), docs: Docs::with_summary("The condition to assert"), + variadic: false, }], return_type: types.void(), phase: Phase::Independent, @@ -251,6 +253,7 @@ pub fn type_check( typeref: types.string(), name: "message".into(), docs: Docs::with_summary("The message to throw"), + variadic: false, }], return_type: types.void(), phase: Phase::Independent, @@ -268,6 +271,7 @@ pub fn type_check( typeref: types.string(), name: "message".into(), docs: Docs::with_summary("The message to panic with"), + variadic: false, }], return_type: types.void(), phase: Phase::Independent, diff --git a/libs/wingc/src/parser.rs b/libs/wingc/src/parser.rs index bd35f438f7b..71428f93a3e 100644 --- a/libs/wingc/src/parser.rs +++ b/libs/wingc/src/parser.rs @@ -1085,16 +1085,16 @@ impl<'s> Parser<'s> { fn build_parameter_list(&self, parameter_list_node: &Node, phase: Phase) -> DiagnosticResult> { let mut res = vec![]; let mut cursor = parameter_list_node.walk(); - for parameter_definition_node in parameter_list_node.named_children(&mut cursor) { - if parameter_definition_node.is_extra() { + for definition_node in parameter_list_node.named_children(&mut cursor) { + if definition_node.is_extra() { continue; } res.push(FunctionParameter { - name: self.check_reserved_symbol(¶meter_definition_node.child_by_field_name("name").unwrap())?, - type_annotation: self - .build_type_annotation(¶meter_definition_node.child_by_field_name("type").unwrap(), phase)?, - reassignable: parameter_definition_node.child_by_field_name("reassignable").is_some(), + name: self.check_reserved_symbol(&definition_node.child_by_field_name("name").unwrap())?, + type_annotation: self.build_type_annotation(&definition_node.child_by_field_name("type").unwrap(), phase)?, + reassignable: definition_node.child_by_field_name("reassignable").is_some(), + variadic: definition_node.child_by_field_name("variadic").is_some(), }); } @@ -1195,6 +1195,7 @@ impl<'s> Parser<'s> { name: "".into(), type_annotation: t, reassignable: false, + variadic: false, }) } diff --git a/libs/wingc/src/type_check.rs b/libs/wingc/src/type_check.rs index 36a1b5b63c7..bd921f48c12 100644 --- a/libs/wingc/src/type_check.rs +++ b/libs/wingc/src/type_check.rs @@ -688,6 +688,7 @@ pub struct FunctionParameter { pub name: String, pub typeref: TypeRef, pub docs: Docs, + pub variadic: bool, } #[derive(Clone, Debug)] @@ -718,7 +719,7 @@ impl FunctionSignature { .iter() .rev() // TODO - as a hack we treat `anything` arguments like optionals so that () => {} can be a subtype of (any) => {} - .take_while(|arg| arg.typeref.is_option() || arg.typeref.is_struct() || arg.typeref.is_anything()) + .take_while(|arg| arg.typeref.is_option() || arg.typeref.is_struct() || arg.typeref.is_anything() || arg.variadic) .count(); self.parameters.len() - num_optionals @@ -2057,7 +2058,20 @@ impl<'a> TypeChecker<'a> { .count(); // Verify arity - let arg_count = arg_list.pos_args.len() + (if arg_list.named_args.is_empty() { 0 } else { 1 }); + + // check if there is a variadic parameter, get its index + let variadic_index = func_sig.parameters.iter().position(|o| o.variadic); + let (index_last_item, arg_count) = if let Some(variadic_index) = variadic_index { + ( + variadic_index, + (variadic_index + 1) + (if arg_list.named_args.is_empty() { 0 } else { 1 }), + ) + } else { + ( + arg_list_types.pos_args.len(), + (arg_list_types.pos_args.len()) + (if arg_list.named_args.is_empty() { 0 } else { 1 }), + ) + }; let min_args = func_sig.parameters.len() - num_optionals; let max_args = func_sig.parameters.len(); if arg_count < min_args || arg_count > max_args { @@ -2076,10 +2090,41 @@ impl<'a> TypeChecker<'a> { .iter() .take(func_sig.parameters.len() - num_optionals); - // Verify passed positional arguments match the function's parameter types - for (arg_expr, arg_type, param) in izip!(arg_list.pos_args.iter(), arg_list_types.pos_args.iter(), params) { - self.validate_type(*arg_type, param.typeref, arg_expr); - } + if index_last_item == arg_list_types.pos_args.len() { + for (arg_expr, arg_type, param) in izip!(arg_list.pos_args.iter(), arg_list_types.pos_args.iter(), params) { + self.validate_type(*arg_type, param.typeref, arg_expr); + } + } else { + let mut new_arg_list: Vec<&Expr> = Vec::new(); + let mut new_arg_list_types: Vec = Vec::new(); + for i in 0..index_last_item { + new_arg_list.push(arg_list.pos_args.get(i).unwrap()); + new_arg_list_types.push(*arg_list_types.pos_args.get(i).unwrap()); + } + + let mut variadic_arg_list: Vec<&Expr> = Vec::new(); + let variadic_arg_types = *arg_list_types.pos_args.get(index_last_item).unwrap(); + for i in index_last_item..arg_list.pos_args.len() { + let variadic_arg = arg_list.pos_args.get(i).unwrap(); + if !variadic_arg_types.is_same_type_as(arg_list_types.pos_args.get(i).unwrap()) { + let error = format!( + "Expected type to be {}, but got {} instead.", + variadic_arg_types, + arg_list_types.pos_args.get(i).unwrap() + ); + self.spanned_error(&variadic_arg.span, error); + } + variadic_arg_list.push(variadic_arg); + } + let variadic_array_inner_type = *arg_list_types.pos_args.get(index_last_item).unwrap(); + for (arg_expr, arg_type, param) in izip!(new_arg_list.iter(), new_arg_list_types.iter(), params) { + self.validate_type(*arg_type, param.typeref, *arg_expr); + } + // assert that each the extra args are of the same type as the variadic array type + for arg_expr in variadic_arg_list.iter() { + self.validate_type(variadic_array_inner_type, variadic_array_inner_type, *arg_expr); + } + }; None } @@ -2288,11 +2333,31 @@ impl<'a> TypeChecker<'a> { } TypeAnnotationKind::Function(ast_sig) => { let mut parameters = vec![]; + for i in 0..ast_sig.parameters.len() { + let p = ast_sig.parameters.get(i).unwrap(); + if p.variadic && i != (ast_sig.parameters.len() - 1) { + self.spanned_error( + &ast_sig.parameters.get(i).unwrap().name.span, + "Variadic parameters must always be the last parameter in a function.".to_string(), + ); + } + + if p.variadic { + match &p.type_annotation.kind { + TypeAnnotationKind::Array(_) | TypeAnnotationKind::MutArray(_) => {} + _ => self.spanned_error( + &ast_sig.parameters.get(i).unwrap().name.span, + "Variadic parameters must be type Array or MutArray.".to_string(), + ), + }; + } + } for p in ast_sig.parameters.iter() { parameters.push(FunctionParameter { name: p.name.name.clone(), typeref: self.resolve_type_annotation(&p.type_annotation, env), docs: Docs::default(), + variadic: p.variadic, }); } let sig = FunctionSignature { @@ -3554,6 +3619,7 @@ impl<'a> TypeChecker<'a> { name: param.name.clone(), docs: param.docs.clone(), typeref: self.get_concrete_type_for_generic(param.typeref, &types_map), + variadic: param.variadic, }) .collect(); @@ -4514,6 +4580,7 @@ mod tests { typeref: num, docs: Docs::default(), name: "p1".into(), + variadic: false, }], void, Phase::Inflight, @@ -4523,6 +4590,7 @@ mod tests { typeref: string, docs: Docs::default(), name: "p1".into(), + variadic: false, }], void, Phase::Inflight, @@ -4561,6 +4629,7 @@ mod tests { typeref: string, docs: Docs::default(), name: "p1".into(), + variadic: false, }], void, Phase::Inflight, @@ -4570,6 +4639,7 @@ mod tests { typeref: opt_string, docs: Docs::default(), name: "p1".into(), + variadic: false, }], void, Phase::Inflight, diff --git a/libs/wingc/src/type_check/jsii_importer.rs b/libs/wingc/src/type_check/jsii_importer.rs index f5d12ed52fd..7bb42ddea08 100644 --- a/libs/wingc/src/type_check/jsii_importer.rs +++ b/libs/wingc/src/type_check/jsii_importer.rs @@ -467,6 +467,7 @@ impl<'a> JsiiImporter<'a> { name: param.name.clone(), typeref: self.parameter_to_wing_type(¶m), docs: Docs::from(¶m.docs), + variadic: param.variadic.unwrap_or(false), }); } } @@ -726,6 +727,7 @@ impl<'a> JsiiImporter<'a> { name: param.name.clone(), typeref: self.parameter_to_wing_type(¶m), docs: Docs::from(¶m.docs), + variadic: param.variadic.unwrap_or(false), }); } } diff --git a/tools/hangar/__snapshots__/invalid.ts.snap b/tools/hangar/__snapshots__/invalid.ts.snap index d419aaddb76..942e2011433 100644 --- a/tools/hangar/__snapshots__/invalid.ts.snap +++ b/tools/hangar/__snapshots__/invalid.ts.snap @@ -702,6 +702,63 @@ error: Expected function return type +Tests 1 failed (1) +Test Files 1 failed (1) +Duration " +`; + +exports[`function_variadic_definition.w 1`] = ` +"error: Unknown parser error + --> ../../../examples/tests/invalid/function_variadic_definition.w:15:13 + | +15 | f5(args: 1, 2); + | ^ Unknown parser error + + +error: Variadic parameters must always be the last parameter in a function. + --> ../../../examples/tests/invalid/function_variadic_definition.w:1:14 + | +1 | let f1 = (...args: Array, x:num) => {}; + | ^^^^ Variadic parameters must always be the last parameter in a function. + + +error: Variadic parameters must always be the last parameter in a function. + --> ../../../examples/tests/invalid/function_variadic_definition.w:4:14 + | +4 | let f2 = (...nums: Array, ...strs: Array) => {}; + | ^^^^ Variadic parameters must always be the last parameter in a function. + + +error: Expected type to be num, but got bool instead. + --> ../../../examples/tests/invalid/function_variadic_definition.w:8:7 + | +8 | f3(1, true, 2); + | ^^^^ Expected type to be num, but got bool instead. + + +error: Variadic parameters must be type Array or MutArray. + --> ../../../examples/tests/invalid/function_variadic_definition.w:11:14 + | +11 | let f4 = (...args: Set) => {}; + | ^^^^ Variadic parameters must be type Array or MutArray. + + +error: No named arguments expected + --> ../../../examples/tests/invalid/function_variadic_definition.w:15:1 + | +15 | f5(args: 1, 2); + | ^^^^^^^^^^^^^^ No named arguments expected + + +error: Expected type to be Bucket, but got bool instead. + --> ../../../examples/tests/invalid/function_variadic_definition.w:24:21 + | +24 | funcBucket(bucket1, true, bucket2); + | ^^^^ Expected type to be Bucket, but got bool instead. + + + + Tests 1 failed (1) Test Files 1 failed (1) Duration " diff --git a/tools/hangar/__snapshots__/test_corpus/valid/function_variadic_arguments.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/function_variadic_arguments.w_compile_tf-aws.md new file mode 100644 index 00000000000..6ed1f1cfddf --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/function_variadic_arguments.w_compile_tf-aws.md @@ -0,0 +1,164 @@ +# [function_variadic_arguments.w](../../../../../examples/tests/valid/function_variadic_arguments.w) | compile | tf-aws + +## main.tf.json +```json +{ + "//": { + "metadata": { + "backend": "local", + "stackName": "root", + "version": "0.17.0" + }, + "outputs": { + "root": { + "Default": { + "cloud.TestRunner": { + "TestFunctionArns": "WING_TEST_RUNNER_FUNCTION_ARNS" + } + } + } + } + }, + "output": { + "WING_TEST_RUNNER_FUNCTION_ARNS": { + "value": "[]" + } + }, + "provider": { + "aws": [ + {} + ] + }, + "resource": { + "aws_s3_bucket": { + "bucket1": { + "//": { + "metadata": { + "path": "root/Default/Default/bucket1/Default", + "uniqueId": "bucket1" + } + }, + "bucket_prefix": "bucket1-c81ed215-", + "force_destroy": false + }, + "bucket2": { + "//": { + "metadata": { + "path": "root/Default/Default/bucket2/Default", + "uniqueId": "bucket2" + } + }, + "bucket_prefix": "bucket2-c83a0be6-", + "force_destroy": false + } + }, + "aws_s3_bucket_public_access_block": { + "bucket1_PublicAccessBlock_01FA69AD": { + "//": { + "metadata": { + "path": "root/Default/Default/bucket1/PublicAccessBlock", + "uniqueId": "bucket1_PublicAccessBlock_01FA69AD" + } + }, + "block_public_acls": true, + "block_public_policy": true, + "bucket": "${aws_s3_bucket.bucket1.bucket}", + "ignore_public_acls": true, + "restrict_public_buckets": true + }, + "bucket2_PublicAccessBlock_063D91B9": { + "//": { + "metadata": { + "path": "root/Default/Default/bucket2/PublicAccessBlock", + "uniqueId": "bucket2_PublicAccessBlock_063D91B9" + } + }, + "block_public_acls": true, + "block_public_policy": true, + "bucket": "${aws_s3_bucket.bucket2.bucket}", + "ignore_public_acls": true, + "restrict_public_buckets": true + } + }, + "aws_s3_bucket_server_side_encryption_configuration": { + "bucket1_Encryption_4417F366": { + "//": { + "metadata": { + "path": "root/Default/Default/bucket1/Encryption", + "uniqueId": "bucket1_Encryption_4417F366" + } + }, + "bucket": "${aws_s3_bucket.bucket1.bucket}", + "rule": [ + { + "apply_server_side_encryption_by_default": { + "sse_algorithm": "AES256" + } + } + ] + }, + "bucket2_Encryption_6F02F3D7": { + "//": { + "metadata": { + "path": "root/Default/Default/bucket2/Encryption", + "uniqueId": "bucket2_Encryption_6F02F3D7" + } + }, + "bucket": "${aws_s3_bucket.bucket2.bucket}", + "rule": [ + { + "apply_server_side_encryption_by_default": { + "sse_algorithm": "AES256" + } + } + ] + } + } + } +} +``` + +## preflight.js +```js +const $stdlib = require('@winglang/sdk'); +const $outdir = process.env.WING_SYNTH_DIR ?? "."; +const std = $stdlib.std; +const $wing_is_test = process.env.WING_IS_TEST === "true"; +const cloud = require('@winglang/sdk').cloud; +class $Root extends $stdlib.std.Resource { + constructor(scope, id) { + super(scope, id); + const bucket1 = this.node.root.newAbstract("@winglang/sdk.cloud.Bucket",this,"bucket1"); + const bucket2 = this.node.root.newAbstract("@winglang/sdk.cloud.Bucket",this,"bucket2"); + const funcBucket = ((...buckets) => { + {((cond) => {if (!cond) throw new Error("assertion failed: buckets.length == 2")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(buckets.length,2)))}; + }); + (funcBucket(bucket1,bucket2)); + const func1 = ((x, y, ...args) => { + {((cond) => {if (!cond) throw new Error("assertion failed: x == 1")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(x,1)))}; + {((cond) => {if (!cond) throw new Error("assertion failed: y == \"something\" || y == nil")})(((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(y,"something")) || (((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(y,undefined))))}; + {((cond) => {if (!cond) throw new Error("assertion failed: args.length == 4")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(args.length,4)))}; + for (const i of args) { + {((cond) => {if (!cond) throw new Error("assertion failed: i > 0 && i < 5")})(((i > 0) && (i < 5)))}; + } + (args.push(10)); + {((cond) => {if (!cond) throw new Error("assertion failed: args.at(4) == 10")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })((args.at(4)),10)))}; + }); + (func1(1,"something",1,2,3,4)); + (func1(1,undefined,1,2,3,4)); + const addNums = ((...nums) => { + let total = 0; + for (const n of nums) { + total = (total + n); + } + return total; + }); + {((cond) => {if (!cond) throw new Error("assertion failed: addNums(1, 2, 3) == 6")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })((addNums(1,2,3)),6)))}; + {((cond) => {if (!cond) throw new Error("assertion failed: addNums() == 0")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })((addNums()),0)))}; + } +} +const $App = $stdlib.core.App.for(process.env.WING_TARGET); +new $App({ outdir: $outdir, name: "function_variadic_arguments", rootConstruct: $Root, plugins: $plugins, isTestEnvironment: $wing_is_test }).synth(); + +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/function_variadic_arguments.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/function_variadic_arguments.w_test_sim.md new file mode 100644 index 00000000000..0029bed2484 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/function_variadic_arguments.w_test_sim.md @@ -0,0 +1,12 @@ +# [function_variadic_arguments.w](../../../../../examples/tests/valid/function_variadic_arguments.w) | test | sim + +## stdout.log +```log +pass ─ function_variadic_arguments.wsim (no tests) + + +Tests 1 passed (1) +Test Files 1 passed (1) +Duration +``` +