risingwavelabs/risingwave · error

expected literal

Error message

expected literal

What it means

When parsing the attribute list of a `#[function(...)]` declaration, each recognized key (`batch_fn`, `state`, `prebuild`, etc.) must be given a string literal value via `get_value()`. If the value after `=` is an expression but not a literal (e.g. a path, macro call, or concatenation), the parser emits this spanned error.

Source

Thrown at src/expr/macro/src/parse.rs:62

        parsed.name = name.trim().to_owned();
        parsed.args = if args.is_empty() {
            vec![]
        } else {
            args.split(',').map(|s| s.trim().to_owned()).collect()
        };
        parsed.ret = ret.trim().to_owned();
        parsed.is_table_function = is_table_function;

        if input.parse::<Token![,]>().is_err() {
            return Ok(parsed);
        }

        let metas = input.parse_terminated(syn::Meta::parse, Token![,])?;
        for meta in metas {
            let get_value = || {
                let kv = meta.require_name_value()?;
                let syn::Expr::Lit(lit) = &kv.value else {
                    return Err(Error::new(kv.value.span(), "expected literal"));
                };
                let syn::Lit::Str(lit) = &lit.lit else {
                    return Err(Error::new(kv.value.span(), "expected string literal"));
                };
                Ok(lit.value())
            };
            if meta.path().is_ident("batch_fn") {
                parsed.batch_fn = Some(get_value()?);
            } else if meta.path().is_ident("state") {
                parsed.state = Some(get_value()?);
            } else if meta.path().is_ident("init_state") {
                parsed.init_state = Some(get_value()?);
            } else if meta.path().is_ident("prebuild") {
                parsed.prebuild = Some(get_value()?);
            } else if meta.path().is_ident("type_infer") {
                parsed.type_infer = Some(get_value()?);
            } else if meta.path().is_ident("generic") {
                parsed.generic = Some(get_value()?);

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Replace the expression with a plain string literal: `batch_fn = "..."`.
  2. Inline whatever the constant or macro produced as a literal string.
  3. Compute the value at build time with a build script only if truly dynamic — the attribute itself must stay a literal.

Example fix

// before
#[function("my_add(int4, int4)", batch_fn = MY_BATCH_FN)]

// after
#[function("my_add(int4, int4)", batch_fn = "my_add_batch")]
Defensive patterns

Strategy: validation

Validate before calling

// Attribute values must be plain string literals
macro_rules! check_attr { ($k:literal = $v:expr) => { const { assert!(matches!($v, _: &'static str)); } } }
// Simplest guard: review that every key=value uses quotes:
//   GOOD: batch_fn = "name"   BAD: batch_fn = some_const

Prevention

When it happens

Trigger: Writing `#[function("sig(int4)", batch_fn = some_const)]`, `prebuild = concat!("a", "b")`, or any non-literal expression after a key that requires a string value.

Common situations: Developers trying to share/configure attribute values through constants or macro-generated strings, which Rust proc-macro attribute parsing here does not support.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/6c42e9b48660ad1c. Report an issue: GitHub.