rust-lang/rust · error · syn::Error
attributes must be outer attributes (`///`), not inner attri
Error message
attributes must be outer attributes (`///`), not inner attributes
What it means
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.
Source
Thrown at compiler/rustc_macros/src/query.rs:23
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
AttrStyle, Attribute, Error, Expr, Ident, Pat, ReturnType, Token, Type, braced, parenthesized,
parse_macro_input, token,
};
mod kw {
syn::custom_keyword!(non_query);
syn::custom_keyword!(query);
}
/// Ensures only doc comment attributes are used
fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {
let inner = |attr: Attribute| {
if !attr.path().is_ident("doc") {
Err(Error::new(attr.span(), "attributes not supported on queries"))
} else if attr.style != AttrStyle::Outer {
Err(Error::new(
attr.span(),
"attributes must be outer attributes (`///`), not inner attributes",
))
} else {
Ok(attr)
}
};
attrs.into_iter().map(inner).collect()
}
/// Declaration of a compiler query.
///
/// ```ignore (illustrative)
/// /// Doc comment for `my_query`.
/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doc_comments
/// query my_query(key: DefId) -> Value { anon }
/// // ^^^^^^^^ name
/// // ^^^ key_patView on GitHub (pinned to 22057b88b0)
Solutions
- Replace //! with /// above the query/non_query declaration
- Ensure each query has its own /// doc comment rather than relying on a //! block
Example fix
// before
//! Documents my_query.
query my_query(key: DefId) -> Value { eval_always }
// after
/// Documents my_query.
query my_query(key: DefId) -> Value { eval_always } Defensive patterns
Strategy: validation
Validate before calling
// 'attributes must be outer attributes (`///`), not inner attributes'
// — fired by the queries macro when you write #![...] (inner) instead of
// /// (outer) on a query. Validate source before expansion.
fn reject_inner_attrs_in_queries(src: &str) -> Result<(), String> {
for (i, line) in src.lines().enumerate() {
let t = line.trim_start();
if t.starts_with("#![") || t.starts_with("#![ ") {
return Err(format!(
"line {}: inner attribute `#![...]` is not allowed here; use outer `///`",
i + 1
));
}
}
Ok(())
} Type guard
enum AttrStyle { Outer, Inner, NotAttr }
fn classify_first_attr(line: &str) -> AttrStyle {
let t = line.trim_start();
if t.starts_with("///") || t.starts_with("//!") && t.trim_end_matches('/').trim().is_empty() == false {
// outer doc comment OR (handled below) inner doc comment
}
let t = line.trim_start();
if t.starts_with("///") || t.starts_with("#[") { AttrStyle::Outer }
else if t.starts_with("#![") || t.starts_with("//!") { AttrStyle::Inner }
else { AttrStyle::NotAttr }
}
fn is_outer_or_none(line: &str) -> bool {
!matches!(classify_first_attr(line), AttrStyle::Inner)
} Try / catch
// Compile-time error from the queries macro; prevent via pre-commit lint:
#[test]
fn queries_use_only_outer_attrs() {
let src = include_str!("../src/compiler/rustc_query_impl.rs");
for (i, line) in src.lines().enumerate() {
let t = line.trim_start();
if t.starts_with("#![") || t.starts_with("//!") {
panic!("line {}: inner attribute/comment not supported on queries; use `///`", i + 1);
}
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- attributes not supported on queries
- {env_var} env var: {err}
- duplicate modifier
- unknown query modifier
- Expected a string literal
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/0eee84e703bab76e.json.
Report an issue: GitHub.