{"id":"0eee84e703bab76e","repo":"rust-lang/rust","slug":"attributes-must-be-outer-attributes-not-i","errorCode":null,"errorMessage":"attributes must be outer attributes (`///`), not inner attributes","messagePattern":"attributes must be outer attributes \\(`///`\\), not inner attributes","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"compiler/rustc_macros/src/query.rs","lineNumber":23,"sourceCode":"use syn::punctuated::Punctuated;\nuse syn::spanned::Spanned;\nuse syn::{\n    AttrStyle, Attribute, Error, Expr, Ident, Pat, ReturnType, Token, Type, braced, parenthesized,\n    parse_macro_input, token,\n};\n\nmod kw {\n    syn::custom_keyword!(non_query);\n    syn::custom_keyword!(query);\n}\n\n/// Ensures only doc comment attributes are used\nfn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {\n    let inner = |attr: Attribute| {\n        if !attr.path().is_ident(\"doc\") {\n            Err(Error::new(attr.span(), \"attributes not supported on queries\"))\n        } else if attr.style != AttrStyle::Outer {\n            Err(Error::new(\n                attr.span(),\n                \"attributes must be outer attributes (`///`), not inner attributes\",\n            ))\n        } else {\n            Ok(attr)\n        }\n    };\n    attrs.into_iter().map(inner).collect()\n}\n\n/// Declaration of a compiler query.\n///\n/// ```ignore (illustrative)\n/// /// Doc comment for `my_query`.\n/// //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^              doc_comments\n/// query my_query(key: DefId) -> Value { anon }\n/// //    ^^^^^^^^                               name\n/// //             ^^^                           key_pat","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_macros/src/query.rs#L5-L41","documentation":"Also raised by check_attributes in rustc_macros::query, in the else-branch after confirming the attribute path is doc. The doc attribute must use outer style (///) rather than inner style (//!). On query/non_query declarations only a leading doc comment makes sense; an inner doc comment is a structural mistake because there is no item body for it to document.","triggerScenarios":"An attribute with path doc and style != AttrStyle::Outer is parsed above a query or non_query declaration. In practice this is a //! comment placed where a /// comment is expected, because Attribute::parse_outer still picks up some inner-style tokens in certain macro contexts, or the user wrote //! above the declaration.","commonSituations":"Editing rustc query definition files and using //! (module doc) style instead of /// (item doc) above a query; converting a module-level comment to an item comment and forgetting to flip the slashes.","solutions":["Replace //! with /// above the query/non_query declaration","Ensure each query has its own /// doc comment rather than relying on a //! block"],"exampleFix":"// before\n//! Documents my_query.\nquery my_query(key: DefId) -> Value { eval_always }\n// after\n/// Documents my_query.\nquery my_query(key: DefId) -> Value { eval_always }","handlingStrategy":"validation","validationCode":"// 'attributes must be outer attributes (`///`), not inner attributes'\n// — fired by the queries macro when you write #![...] (inner) instead of\n// /// (outer) on a query. Validate source before expansion.\nfn reject_inner_attrs_in_queries(src: &str) -> Result<(), String> {\n    for (i, line) in src.lines().enumerate() {\n        let t = line.trim_start();\n        if t.starts_with(\"#![\") || t.starts_with(\"#![ \") {\n            return Err(format!(\n                \"line {}: inner attribute `#![...]` is not allowed here; use outer `///`\",\n                i + 1\n            ));\n        }\n    }\n    Ok(())\n}","typeGuard":"enum AttrStyle { Outer, Inner, NotAttr }\n\nfn classify_first_attr(line: &str) -> AttrStyle {\n    let t = line.trim_start();\n    if t.starts_with(\"///\") || t.starts_with(\"//!\") && t.trim_end_matches('/').trim().is_empty() == false {\n        // outer doc comment OR (handled below) inner doc comment\n    }\n    let t = line.trim_start();\n    if t.starts_with(\"///\") || t.starts_with(\"#[\") { AttrStyle::Outer }\n    else if t.starts_with(\"#![\") || t.starts_with(\"//!\") { AttrStyle::Inner }\n    else { AttrStyle::NotAttr }\n}\n\nfn is_outer_or_none(line: &str) -> bool {\n    !matches!(classify_first_attr(line), AttrStyle::Inner)\n}","tryCatchPattern":"// Compile-time error from the queries macro; prevent via pre-commit lint:\n#[test]\nfn queries_use_only_outer_attrs() {\n    let src = include_str!(\"../src/compiler/rustc_query_impl.rs\");\n    for (i, line) in src.lines().enumerate() {\n        let t = line.trim_start();\n        if t.starts_with(\"#![\") || t.starts_with(\"//!\") {\n            panic!(\"line {}: inner attribute/comment not supported on queries; use `///`\", i + 1);\n        }\n    }\n}","preventionTips":["On query definitions and items inside rustc_queries! { } blocks, always use outer doc comments (///) and outer attributes (#[...]); never inner (//! or #![...]).","Add a pre-commit grep that rejects lines starting with #![ or //! inside the queries block.","Read the macro's docstring once when onboarding — it documents the outer-attributes-only rule.","Configure rustfmt to leave these blocks untouched so reviewers can spot stray inner attrs.","During code review, specifically scan rustc_queries! blocks for inner-attribute syntax."],"tags":["proc-macro","rustc-internal","query-system","macro-parse"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}