rustfmt eats _ in underscore_imports (a.k.a. impl-only-use)

Created on 2 Aug 2018  路  7Comments  路  Source: rust-lang/rustfmt

With an input file like

#![feature(underscore_imports)]

use std::iter::Iterator as _;

fn main() {}

running rustfmt on it produces

#![feature(underscore_imports)]

use std::iter::Iterator as ;

fn main() {}

Version:

rustfmt 0.9.0-nightly (69ad879d 2018-07-27)

Tracking issue for the unstable feature: https://github.com/rust-lang/rust/issues/48216

bug good first issue

Most helpful comment

(FYI I had a look at this over the weekend. It was a steeper learning curve than I had thought, but I'll give it another try)

All 7 comments

cc @max-sixty the universe provides! This would be a great issue to start on. Look in imports.rs and look for the code dealing with aliasing. You'll want to look at libsyntax/ast.rs in the rust repo to find out how the underscore is represented, and then take that into account in the rustfmt code.

(FYI I had a look at this over the weekend. It was a steeper learning curve than I had thought, but I'll give it another try)

What's the easiest way for me to discover how ast parses the _? I'm struggling to even view the output of a string of code. I've read the docs but let me know if I'm missing something obvious. Thanks

@max-sixty I would run rustfmt on a very simple program using _ and then dump the entire AST (println). For the code, I would look at token.rs in the compiler - iirc, _ vs other identifiers is handled during lexing, not parsing (it's been a while though, I might be wrong).

For the sake of progress - and feedback if I'm doing something bad, I ran this file:

// #![allow(unused_variables)]
// #![allow(unused_imports)]
extern crate syntax;
use std::path::PathBuf;
use syntax::with_globals;

use syntax::codemap::FilePathMapping;
use syntax::parse::{filemap_to_stream, ParseSess};

fn main() {
    let parse_session = ParseSess::new(FilePathMapping::empty());
    // let p = Path::new(file!());
    // let string = "use std::iter::Iterator as _;".to_string();
    with_globals(|| {
        let stream = filemap_to_stream(
            &parse_session,
            parse_session
                .codemap()
                .new_filemap(PathBuf::from("").into(), "use a::b as _".to_string()),
            None,
        );

        println!("{:?}", stream)
    });
}

...which generated this output:

TokenStream { kind: Stream([TokenStream { kind: Tree(Token(Span { lo: BytePos(0), hi: BytePos(3), ctxt: #0 }, Ident(use#0, false))) }, TokenStream { kind: JointTree(Token(Span { lo: BytePos(4), hi: BytePos(5), ctxt: #0 }, Ident(a#0, false))) }, TokenStream { kind: Tree(Token(Span { lo: BytePos(5), hi: BytePos(7), ctxt: #0 }, ModSep)) }, TokenStream { kind: Tree(Token(Span { lo: BytePos(7), hi: BytePos(8), ctxt: #0 }, Ident(b#0, false))) }, TokenStream { kind: Tree(Token(Span { lo: BytePos(9), hi: BytePos(11), ctxt: #0 }, Ident(as#0, false))) }, TokenStream { kind: Tree(Token(Span { lo: BytePos(12), hi: BytePos(13), ctxt: #0 }, Ident(_#0, false))) }]) }

...suggesting that _ is parsed as Ident(_#0, false)

Reading through the code, my hypothesis is this section is passing an empty ident as an alias.

https://github.com/rust-lang-nursery/rustfmt/blob/master/src/imports.rs#L352-L357

I'm not sure how to debug this though. Let me know if you have any guidance from here!

I would look at rewrite_ident - I think it might be formatting _ as ``. Probably insert some printlns to confirm.

Was this page helpful?
0 / 5 - 0 ratings