denoland/deno · error · syn::Error
Expected a function or impl block for #[op2]
Error message
Expected a function or impl block for #[op2]
What it means
The `#[op2]` macro entry point (libs/ops/op2/mod.rs) tries to parse the annotated item first as `syn::ItemFn`, then as `syn::ItemImpl`. If both parses fail it combines the two underlying parse errors with this message, so the real cause is usually visible in the combined error chain below it.
Source
Thrown at libs/ops/op2/mod.rs:179
syn::Error::new(span, msg)
}
}
pub type V8MappingError = &'static str;
/// Generate the op2 macro expansion.
pub(crate) fn op2(
attr: TokenStream,
item: TokenStream,
) -> Result<TokenStream, Op2Error> {
let func = match parse2::<ItemFn>(item.clone()) {
Ok(func) => func,
Err(fn_err) => match parse2::<syn::ItemImpl>(item) {
Ok(impl_block) => {
return object_wrap::generate_impl_ops(attr, impl_block);
}
Err(impl_err) => {
let mut err = syn::Error::new(
fn_err.span(),
"Expected a function or impl block for #[op2]",
);
err.combine(fn_err);
err.combine(impl_err);
return Err(err.into());
}
},
};
let span = attr.span();
let metas =
Punctuated::<config::CustomMeta, syn::Token![,]>::parse_terminated
.parse2(attr)?
.into_iter()
.collect::<Vec<_>>();
View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Move `#[op2]` so it annotates a plain `fn` (for a standalone op) or an `impl` block (for wrapped-object methods).
- If it is already on a function, read the combined parse errors (fn_err/impl_err are attached) — there is usually a syntax error in the signature or body that breaks parsing.
- Remove the attribute from non-op items such as structs, consts, or traits.
Example fix
// before
#[op2]
struct OpStateHolder { /* ... */ }
// after
#[op2(fast)]
fn op_get_state() -> u32 { /* ... */ } Defensive patterns
Strategy: validation
Validate before calling
// CI: `cargo check` catches this instantly. Structural guard if you generate code: // only emit `#[op2]` onto `fn` items or `impl` blocks, never onto other items.
Prevention
- Apply `#[op2]` immediately above a function definition or an impl block — nowhere else.
- If the error appears on a real function, suspect a syntax error in the body and read the attached parse errors.
When it happens
Trigger: Attaching `#[op2]` to anything that is not a free function or an `impl` block: structs, enums, traits, `const`s, `type` aliases, `trait` default methods, or a function with syntax errors that also breaks ItemFn parsing.
Common situations: Pasting the attribute above the wrong declaration while wiring up new ops; a syntax error inside the function body that makes `parse2::<ItemFn>` fail even though the item looks like a function.
Related errors
- Only ASCII keys are supported
- The flags for this attribute were not sorted alphabetically.
- expected attribute arguments in parentheses: `{}(...)`
- duplicate `default` argument
- duplicate `options` argument
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/d0406e16acf06c15.
Report an issue: GitHub.