{"record":{"id":"d0406e16acf06c15","repo":"denoland/deno","slug":"expected-a-function-or-impl-block-for-op2","errorCode":null,"errorMessage":"Expected a function or impl block for #[op2]","messagePattern":"Expected a function or impl block for #\\[op2\\]","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"libs/ops/op2/mod.rs","lineNumber":179,"sourceCode":"    syn::Error::new(span, msg)\n  }\n}\n\npub type V8MappingError = &'static str;\n\n/// Generate the op2 macro expansion.\npub(crate) fn op2(\n  attr: TokenStream,\n  item: TokenStream,\n) -> Result<TokenStream, Op2Error> {\n  let func = match parse2::<ItemFn>(item.clone()) {\n    Ok(func) => func,\n    Err(fn_err) => match parse2::<syn::ItemImpl>(item) {\n      Ok(impl_block) => {\n        return object_wrap::generate_impl_ops(attr, impl_block);\n      }\n      Err(impl_err) => {\n        let mut err = syn::Error::new(\n          fn_err.span(),\n          \"Expected a function or impl block for #[op2]\",\n        );\n        err.combine(fn_err);\n        err.combine(impl_err);\n        return Err(err.into());\n      }\n    },\n  };\n\n  let span = attr.span();\n\n  let metas =\n    Punctuated::<config::CustomMeta, syn::Token![,]>::parse_terminated\n      .parse2(attr)?\n      .into_iter()\n      .collect::<Vec<_>>();\n","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/libs/ops/op2/mod.rs#L161-L197","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n#[op2]\nstruct OpStateHolder { /* ... */ }\n\n// after\n#[op2(fast)]\nfn op_get_state() -> u32 { /* ... */ }","handlingStrategy":"validation","validationCode":"// CI: `cargo check` catches this instantly. Structural guard if you generate code:\n// only emit `#[op2]` onto `fn` items or `impl` blocks, never onto other items.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["op2","proc-macro","parse-error","compile-time"],"backgroundTag":"macro-applied-to-wrong-item","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}