{"record":{"id":"96804b3ea1e99af0","repo":"GraphiteEditor/Graphite","slug":"command-functions-take-no-self-they-are-pure-a","errorCode":null,"errorMessage":"command functions take no `self`; they are pure `args… -> Message` translations","messagePattern":"command functions take no `self`; they are pure `args… -> Message` translations","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"proc-macros/src/editor_commands.rs","lineNumber":52,"sourceCode":"\tfor function in &functions {\n\t\tfor attr in &function.attrs {\n\t\t\tif !attr.path().is_ident(\"doc\") {\n\t\t\t\treturn Err(Error::new(\n\t\t\t\t\tattr.span(),\n\t\t\t\t\t\"command functions may not have attributes; anything that doesn't fit the `fn name(args…) -> Message` contract belongs in a plain impl block\",\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\t\tif !matches!(function.vis, Visibility::Inherited) {\n\t\t\treturn Err(Error::new(\n\t\t\t\tfunction.span(),\n\t\t\t\t\"command functions have no visibility modifier; the macro generates the public JS-facing stub\",\n\t\t\t));\n\t\t}\n\n\t\tlet signature = &function.sig;\n\t\tif let Some(receiver) = signature.receiver() {\n\t\t\treturn Err(Error::new(receiver.span(), \"command functions take no `self`; they are pure `args… -> Message` translations\"));\n\t\t}\n\t\tif !signature.generics.params.is_empty() || signature.asyncness.is_some() || signature.unsafety.is_some() {\n\t\t\treturn Err(Error::new(signature.span(), \"command functions must be plain non-generic, non-async, safe functions\"));\n\t\t}\n\n\t\tlet docs = &function.attrs;\n\t\tlet fn_name = &signature.ident;\n\t\tlet variant = Ident::new(&fn_name.to_string().to_case(Case::Pascal), fn_name.span());\n\t\tlet js_name = Ident::new(&fn_name.to_string().to_case(Case::Camel), fn_name.span());\n\n\t\tlet mut param_names = Vec::new();\n\t\tlet mut param_types = Vec::new();\n\t\tfor parameter in &signature.inputs {\n\t\t\tlet FnArg::Typed(pat_type) = parameter else { unreachable!(\"receiver is rejected above\") };\n\t\t\tlet Pat::Ident(pat_ident) = &*pat_type.pat else {\n\t\t\t\treturn Err(Error::new(pat_type.span(), \"command parameters must be plain identifiers\"));\n\t\t\t};\n\t\t\tparam_names.push(&pat_ident.ident);","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/proc-macros/src/editor_commands.rs#L34-L70","documentation":"Command functions in an #[editor_commands] module are pure `args… -> Message` translations and must not take `self`, `&self`, `&mut self`, or any other receiver. The macro packs the parameters into a generated enum variant, and a receiver has no representation in that data model.","triggerScenarios":"Declaring a method-style function (`fn select(&self, id: u64) -> Message`, `&mut self`, `self: Box<Self>`, etc.) inside the module. The check is `signature.receiver()` returning Some.","commonSituations":"Refactoring methods off an editor-state struct into the commands module and forgetting to strip the receiver, or writing `fn foo(mut self)` while converting a builder-style API into commands.","solutions":["Remove the receiver parameter; pass any needed state explicitly as a normal argument.","If the function needs `&self` context, it is not a command — move it to a plain impl block on the context type.","Check that the return type is still a Message variant translation after the move."],"exampleFix":"// before\n#[editor_commands]\nmod commands {\n\tfn select_layer(&self, layer: u64) -> Message { ... }\n}\n\n// after\n#[editor_commands]\nmod commands {\n\tfn select_layer(layer: u64) -> Message { ... }\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model commands as free functions taking explicit arguments, never methods.","Move context-dependent helpers to impl blocks on the context type outside the macro module."],"tags":["rust","proc-macro","editor-commands","self-receiver"],"backgroundTag":"proc-macro-input-validation","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}