rust-lang/rust · error · syn::Error

Expected a string literal

Error message

Expected a string literal

What it means

Thrown by doc_comment_from_desc (query.rs:309-317) when auto-generating a doc comment for a query that was declared without an explicit doc comment. The function takes the desc block's expression list and requires its first expression to be a string literal (Lit::Str); the match arm at query.rs:312-316 returns Err for anything else. This path is only reached when no /// doc comment is present (query.rs:109-111), because the desc string is used as a fallback description.

Source

Thrown at compiler/rustc_macros/src/query.rs:316

        eval_always: #eval_always,
        feedable: #feedable,
        handle_cycle_error: #handle_cycle_error,
        no_force: #no_force,
        no_hash: #no_hash,
        returns_error_guaranteed: #returns_error_guaranteed,
        separate_provide_extern: #separate_provide_extern,
        // tidy-alphabetical-end
    }
}

fn doc_comment_from_desc(list: &Punctuated<Expr, token::Comma>) -> Result<Attribute> {
    use ::syn::*;
    let mut iter = list.iter();
    let format_str: String = match iter.next() {
        Some(&Expr::Lit(ExprLit { lit: Lit::Str(ref lit_str), .. })) => {
            lit_str.value().replace("`{}`", "{}") // We add them later anyways for consistency
        }
        _ => return Err(Error::new(list.span(), "Expected a string literal")),
    };
    let mut fmt_fragments = format_str.split("{}");
    let mut doc_string = fmt_fragments.next().unwrap().to_string();
    iter.map(::quote::ToTokens::to_token_stream).zip(fmt_fragments).for_each(
        |(tts, next_fmt_fragment)| {
            use ::core::fmt::Write;
            write!(
                &mut doc_string,
                " `{}` {}",
                tts.to_string().replace(" . ", "."),
                next_fmt_fragment,
            )
            .unwrap();
        },
    );
    let doc_string = format!("[query description - consider adding a doc-comment!] {doc_string}");
    Ok(parse_quote! { #[doc = #doc_string] })
}

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Add an explicit /// doc comment above the query, which bypasses the auto-generation path entirely (query.rs:109).
  2. Otherwise ensure the first expression inside the desc { } block is a string literal, e.g. `desc { "computing {}", tcx.item_path(key) }`.
  3. Keep format placeholders as `{}` (the code rewrites `` `{}` `` back to `{}` at query.rs:314, so either form works, but it must be a literal).

Example fix

// before
query type_of(key: DefId) -> Ty<'tcx> {
    desc { tcx.def_path_str(key), "type of {}" }
}

// after
query type_of(key: DefId) -> Ty<'tcx> {
    desc { "type of {}", tcx.def_path_str(key) }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// The macro only accepts a *literal* string token; never a const, ident, or expr.
// Good:  #[query(name = "dep_graph")]
// Bad:   const N = "dep_graph"; #[query(name = N)]
fn assert_literal(s: &'static str) -> &'static str { s }

Type guard

// Narrow to a compile-time &str literal so non-literal expressions fail to compile
fn require_str_literal<S: AsRef<str>>(s: S) -> &str { s.as_ref() }

Prevention

When it happens

Trigger: Declaring a query without a /// doc comment and giving a desc block whose first expression is not a string literal, e.g. `query foo(k: K) -> V { desc { tcx.foo(k), "x" } }` (variable before the format string) or `desc { 42 }`. The first element must be a string literal like `"{}"`.

Common situations: Adding a new query to rustc and forgetting the doc comment while also writing the desc block in an unusual order; using a non-literal format string (e.g. a const or macro result) as the first desc argument. End users never see this; it is a rustc-internal compile error.

Related errors


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/3b30689c8d342b2a.json. Report an issue: GitHub.