rust-lang/rust · critical

file modules must have an attribute to exclude

Error message

file modules must have an attribute to exclude

What it means

Internal panic in `fake_token_stream_for_file_mod`, used to reconstruct a `TokenStream` for a file-based module item (one declared via `mod foo;` and loaded from a separate file, i.e. `ModKind::Loaded(_, Inline::No, ..)`). Such modules are required to carry the inner attribute the caller wants stripped, so `attr_to_exclude` must be `Some`. Passing `None` for a file module violates the function's contract and triggers `.expect`.

Source

Thrown at compiler/rustc_parse/src/lib.rs:316

    }

    let source = pprust::item_to_string(item);
    let filename = FileName::macro_expansion_source_code(&source);
    unwrap_or_emit_fatal(source_str_to_stream(psess, filename, source, Some(item.span)))
}

fn fake_token_stream_for_file_mod(
    psess: &ParseSess,
    item: &ast::Item,
    attr_to_exclude: Option<&ast::Attribute>,
) -> Option<TokenStream> {
    let ast::ItemKind::Mod(_, _, ast::ModKind::Loaded(_, ast::Inline::No { .. }, spans)) =
        &item.kind
    else {
        return None;
    };

    let attr = attr_to_exclude.expect("file modules must have an attribute to exclude");
    assert_eq!(attr.style, ast::AttrStyle::Inner);

    let mut body_tts = Vec::new();
    body_tts.extend(lex_token_trees_for_span(psess, spans.inner_span.until(attr.span))?);
    body_tts.extend(lex_token_trees_for_span(
        psess,
        attr.span.between(spans.inner_span.shrink_to_hi()),
    )?);

    let mut wrapper_tts = Vec::new();
    for attr in item.attrs.iter().filter(|attr| attr.style == ast::AttrStyle::Outer) {
        wrapper_tts.extend(attr.token_trees());
    }
    wrapper_tts.extend(lex_token_trees_for_span(psess, item.span)?);
    let Some(TokenTree::Token(semi, _)) = wrapper_tts.pop() else {
        return None;
    };
    if semi.kind != token::Semi {

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report as a rustc ICE with the crate and the specific file module (`mod foo;`) that triggers it
  2. As a workaround, inline the module (`mod foo { ... }`) to take the `Inline::Yes` path that bypasses this function
  3. Bisect nightly toolchains to locate the introducing commit

Example fix

// before: file module triggers the path
mod foo;
// after: inline the module to sidestep file-mod token reconstruction
mod foo {
    // ...
}
Defensive patterns

Strategy: validation

Validate before calling

use rustc_ast::ast;
fn module_has_exclude_attr(item: &ast::Item) -> bool {
    item.attrs.iter().any(|a| {
        matches!(a.kind, ast::AttrKind::Normal(ref n)
            if matches!(n.item.path.segments.iter().last().map(|s| s.ident.as_str()), Some("no_implicit_prelude") | Some("cfg")))
    })
}
if !module_has_exclude_attr(&mod_item) { return Err("file module lacks exclude attribute"); }

Prevention

When it happens

Trigger: The attribute/macros infrastructure calls `fake_token_stream_for_item` (or `fake_token_stream_for_file_mod`) on a file-module item while passing `attr_to_exclude: None`.

Common situations: Compiler regression in attribute expansion or cfg-attribute processing applied to `mod foo;` file modules; usually after changes to inner-attribute handling.

Related errors


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