Repository: bytecodealliance/wasm-tools (rust)

Issue:
wave: Can't parse function calls with empty arguments
Using [`UntypedFuncCall`](https://docs.rs/wasm-wave/latest/wasm_wave/untyped/struct.UntypedFuncCall.html) wave can't currently parse `f()` and yields the error `empty tuple at 1..3`. I think this is due to [function arguments being parsed as a tuple](https://github.com/bytecodealliance/wasm-tools/blob/6fc5d68b30931db5e9639a5cecf107fdf1bef894/crates/wasm-wave/src/parser.rs#L54) and tuples in general cannot be empty.

cc @lann 

Pull request:
wasm-wave: Accept nullary func calls
Fixes #1816 

Patch:
diff --git a/crates/wasm-wave/src/parser.rs b/crates/wasm-wave/src/parser.rs
index f35e5e34..b7576983 100644
--- a/crates/wasm-wave/src/parser.rs
+++ b/crates/wasm-wave/src/parser.rs
@@ -51,7 +51,13 @@ impl<'source> Parser<'source> {
         let name = self.parse_label()?;
         self.advance()?;
         self.expect_token(Token::ParenOpen)?;
-        let params = self.parse_tuple()?;
+
+        let params = if self.next_is(Token::ParenClose) {
+            self.advance()?;
+            None
+        } else {
+            Some(self.parse_tuple()?)
+        };
         Ok(UntypedFuncCall::new(self.lex.source(), name, params))
     }
 
diff --git a/crates/wasm-wave/src/untyped.rs b/crates/wasm-wave/src/untyped.rs
index 6baf3c1d..2706a7ac 100644
--- a/crates/wasm-wave/src/untyped.rs
+++ b/crates/wasm-wave/src/untyped.rs
@@ -60,11 +60,15 @@ impl std::fmt::Display for UntypedValue<'_> {
 pub struct UntypedFuncCall<'source> {
     source: Cow<'source, str>,
     name: Node,
-    params: Node,
+    params: Option<Node>,
 }
 
 impl<'source> UntypedFuncCall<'source> {
-    pub(crate) fn new(source: impl Into<Cow<'source, str>>, name: Node, params: Node) -> Self {
+    pub(crate) fn new(
+        source: impl Into<Cow<'source, str>>,
+        name: Node,
+        params: Option<Node>,
+    ) -> Self {
         Self {
             source: source.into(),
             name,
@@ -100,8 +104,10 @@ impl<'source> UntypedFuncCall<'source> {
     }
 
     /// Returns the function parameters node.
-    pub fn params_node(&self) -> &Node {
-        &self.params
+    ///
+    /// Returns `None` if the function call has no parameters.
+    pub fn params_node(&self) -> Option<&Node> {
+        self.params.as_ref()
     }
 
     /// Returns the function name.
@@ -117,14 +123,20 @@ impl<'source> UntypedFuncCall<'source> {
         &self,
         types: impl IntoIterator<Item = &'types V::Type>,
     ) -> Result<Vec<V>, ParserError> {
-        self.params.to_wasm_params(types, self.source())
+        match &self.params {
+            Some(params) => params.to_wasm_params(types, self.source()),
+            None => Ok(vec![]),
+        }
     }
 }
 
 impl std::fmt::Display for UntypedFuncCall<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.write_str(self.name.slice(&self.source))?;
-        fmt_node(f, &self.params, &self.source)
+        match &self.params {
+            Some(params) => fmt_node(f, params, &self.source),
+            None => f.write_str("()"),
+        }
     }
 }
 


Test patch:
diff --git a/crates/wasm-wave/tests/ui/accept-nullary.out b/crates/wasm-wave/tests/ui/accept-nullary.out
new file mode 100644
index 00000000..34e32f9e
--- /dev/null
+++ b/crates/wasm-wave/tests/ui/accept-nullary.out
@@ -0,0 +1,1 @@
+nullary()
diff --git a/crates/wasm-wave/tests/ui/accept-nullary.waves b/crates/wasm-wave/tests/ui/accept-nullary.waves
new file mode 100644
index 00000000..10c6c5f7
--- /dev/null
+++ b/crates/wasm-wave/tests/ui/accept-nullary.waves
@@ -0,0 +1,1 @@
+nullary();
\ No newline at end of file
diff --git a/crates/wasm-wave/tests/ui/ui.wit b/crates/wasm-wave/tests/ui/ui.wit
index 435a765b..49ba1427 100644
--- a/crates/wasm-wave/tests/ui/ui.wit
+++ b/crates/wasm-wave/tests/ui/ui.wit
@@ -1,6 +1,7 @@
 package ui:tests;
 
 interface ui {
+    nullary: func();
     %float32: func(val: f32);
     %list-float32: func(vals: list<f32>);
     %float64: func(val: f64);