{"record":{"id":"9ab58d7d0a9e0c6d","repo":"GraphiteEditor/Graphite","slug":"command-functions-have-no-visibility-modifier-the","errorCode":null,"errorMessage":"command functions have no visibility modifier; the macro generates the public JS-facing stub","messagePattern":"command functions have no visibility modifier; the macro generates the public JS-facing stub","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"proc-macros/src/editor_commands.rs","lineNumber":44,"sourceCode":"\t\t\tother => return Err(Error::new(other.span(), \"only `use` imports and command functions may appear in an #[editor_commands] module\")),\n\t\t}\n\t}\n\n\tlet mut variants = TokenStream::new();\n\tlet mut stubs = TokenStream::new();\n\tlet mut arms = TokenStream::new();\n\n\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","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/proc-macros/src/editor_commands.rs#L26-L62","documentation":"Command functions declared inside an #[editor_commands] module must have inherited (private) visibility. The macro itself generates the public JS-facing dispatch stub, so a hand-written `pub`, `pub(crate)`, or `pub(in …)` modifier is a compile error — otherwise callers could bypass the generated stub or the visibility would conflict with it.","triggerScenarios":"Writing `pub fn`, `pub(crate) fn`, or any other visibility modifier on a function inside the #[editor_commands] module. The macro checks `matches!(function.vis, Visibility::Inherited)`.","commonSituations":"Copy-pasting an existing public command implementation from a plain module into the macro module, or instinctively marking new API surface as `pub` because it is called from JS elsewhere in the file.","solutions":["Remove the visibility modifier so the command reads `fn name(args…) -> Message`.","Keep the function private and rely on the macro-generated public stub for external access.","If the function genuinely needs manual publicity outside the command dispatch, move it to a plain impl block instead of the #[editor_commands] module."],"exampleFix":"// before\n#[editor_commands]\nmod commands {\n\tpub fn select_layer(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":["Write commands as private `fn name(args…) -> Message`; the macro publishes the stub.","Add a code-review rule: no visibility keywords inside #[editor_commands] modules.","When copy-pasting a command, strip `pub` first."],"tags":["rust","proc-macro","editor-commands","visibility"],"backgroundTag":"proc-macro-input-validation","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}