Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

quote_* macros lose hygiene, gensym and span information #15962

Closed
huonw opened this issue Jul 24, 2014 · 12 comments · Fixed by #23085
Closed

quote_* macros lose hygiene, gensym and span information #15962

huonw opened this issue Jul 24, 2014 · 12 comments · Fixed by #23085
Labels
A-syntaxext Area: Syntax extensions

Comments

@huonw
Copy link
Member

huonw commented Jul 24, 2014

The quote_* macros stringify everything, which loses vital information.

// gensym.rs
#![feature(plugin_registrar, managed_boxes, quote)]
#![crate_type = "dylib"]

extern crate syntax;
extern crate rustc;

use syntax::ast;
use syntax::codemap::{Span};
use syntax::ext::base;
use syntax::ext::base::{ExtCtxt, MacExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use rustc::plugin::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("test_quote", expand_syntax_ext);
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, _: &[ast::TokenTree]) -> Box<base::MacResult> {
    // expand to `{ let foo = true; foo }`, with a gensym'd foo.
    let ident = token::gensym_ident("foo");
    let decl = quote_stmt!(&mut *cx, let $ident = true;);
    let result = cx.expr_block(cx.block(sp, vec![decl], Some(cx.expr_ident(sp, ident))));

    println!("{}", result);

    MacExpr::new(result)
}
// test_gensym.rs
#![feature(phase)]

#[phase(plugin)] extern crate gensym;

fn main() {
    let a = test_quote!();
}
[...]
test_gensym.rs:6:13: 6:27 error: unresolved name `foo`.
test_gensym.rs:6     let a = test_quote!();
                             ^~~~~~~~~~~~~~
test_gensym.rs:1:1: 7:1 note: in expansion of test_quote!
test_gensym.rs:6:13: 6:27 note: expansion site
error: aborting due to previous error

Beautified/trimmed version of the output of the println!

Expr {
    node: ExprBlock(Block {
        view_items: [],
        stmts: [Spanned {
            node: StmtDecl(Spanned {
                node: DeclLocal(Local {
                    ty: Ty {
                        node: TyInfer,
                        span: Span {
                            lo: BytePos(7677),
                            hi: BytePos(7677),
                            expn_info: None
                        }
                    },
                    pat: Pat {
                        node: PatIdent(BindByValue(MutImmutable), Spanned {
                            node: "foo" (269),
                            span: Span {
                                lo: BytePos(7677),
                                hi: BytePos(7680),
                                expn_info: None
                            }
                        }, None),
                        span: Span {
                            lo: BytePos(7677),
                            hi: BytePos(7680),
                            expn_info: None
                        }
                    },
                    init: Some(Expr {
                        node: ExprLit(Spanned {
                            node: LitBool(true),
                            span: Span {
                                lo: BytePos(83),
                                hi: BytePos(97),
                                expn_info: None
                            }
                        }),
                        span: Span {
                            lo: BytePos(83),
                            hi: BytePos(97),
                            expn_info: None
                        }
                    }),
                    span: Span {
                        lo: BytePos(7677),
                        hi: BytePos(97),
                        expn_info: None
                    },
                    source: LocalLet
                }),
            }, 4294967295),
        }],
        expr: Some(Expr {
            node: ExprPath(Path {
                global: false,
                segments: [PathSegment {
                    identifier: "foo" (268)# 0,
                    lifetimes: [],
                    types: OwnedSlice {
                        {}
                    }
                }]
            }),
        }),
        rules: DefaultBlock,
    }),
}

Of particular attention is the difference in Name between node: "foo" (269)
identifier: "foo" (268)# 0,, and also the 7677 etc. numbers in the spans inside theDeclLocal.

@huonw
Copy link
Member Author

huonw commented Jul 24, 2014

cc @jbclements

@huonw huonw changed the title quote_* macros loose gensym and span information quote_* macros lose gensym and span information Jul 25, 2014
@huonw huonw changed the title quote_* macros lose gensym and span information quote_* macros lose hygiene, gensym and span information Jul 29, 2014
@huonw
Copy link
Member Author

huonw commented Jul 29, 2014

cc #15750

@huonw
Copy link
Member Author

huonw commented Jul 29, 2014

Maybe we could hack something for now by putting the information into the stringified source, e.g. an identifier Ident { name: 10, ctxt: 99 } could be stringified as \x0010,99\x00 (i.e. just use the name and ctxt, not the string, with some special leading byte), and have quoting put the parser into special mode which parses a \x00...\x00 as an identifier.

(Huge hack...)

@pnkfelix
Copy link
Member

pnkfelix commented Aug 1, 2014

@huonw that's actually not a terrible idea, at least as a short term hack ... I don't see any reason why we'd be on the hook to support that interface long term, as long as we (as you say) have quote_expr! be the thing that turns on the special parsing mode.

pnkfelix added a commit to pnkfelix/rust that referenced this issue Aug 1, 2014
huonw added a commit to huonw/rust that referenced this issue Aug 7, 2014
…ccessible names.

This requires avoiding `quote_...!` for constructing the parts of the
__test module, since that stringifies and reinterns the idents, losing
the special gensym'd nature of them. (rust-lang#15962.)
@jbclements
Copy link
Contributor

I agree with @pnkfelix ; the string representation has only ad-hoc structure, so this change is actually improving it somewhat. I would be cautious, though; I can easily imagine this leaking out into userland so that programmers start using this representation as a way of hacking hygiene. There's nothing wrong with hacking hygiene, of course, but this probably isn't the way you'd want to do it.

bors added a commit that referenced this issue Aug 9, 2014
This requires avoiding `quote_...!` for constructing the parts of the
__test module, since that stringifies and reinterns the idents, losing
the special gensym'd nature of them. (#15962.)
@pnkfelix
Copy link
Member

I think I have the aforementioned hack implemented. I hope to have a PR soon.

@pnkfelix
Copy link
Member

cc @larsbergstrom

@larsbergstrom
Copy link
Contributor

Thanks, @pnkfelix!

cc @kmcallister

@pnkfelix
Copy link
Member

@huonw it is possible that I was premature in closing this issue with my PR. In particular, the hack fixes the problem of hygiene on Idents, but ... I don't know about gensym ... and it certainly does not address the span issue, right?

(oh wait, my PR didn't close this issue, I just thought it did...)

@huonw
Copy link
Member Author

huonw commented Aug 14, 2014

Printing names should handle gensym info correctly. The span issue is less pressing, as it's "just" diagnostics (rather than making functionality impossible to express), and could be temporarily replaced by syntax providing a "respanning" Fold that just replaces the DUMMY_SP with some given span.

@kmcallister
Copy link
Contributor

src/libsyntax/ext/quote.rs contains an interesting note about the span issue:

    // We also bind a single value, sp, to ext_cx.call_site()
    //
    // This causes every span in a token-tree quote to be attributed to the
    // call site of the extension using the quote. We can't really do much
    // better since the source of the quote may well be in a library that
    // was not even parsed by this compilation run, that the user has no
    // source code for (eg. in libsyntax, which they're just _using_).
    //
    // The old quasiquoter had an elaborate mechanism for denoting input
    // file locations from which quotes originated; unfortunately this
    // relied on feeding the source string of the quote back into the
    // compiler (which we don't really want to do) and, in any case, only
    // pushed the problem a very small step further back: an error
    // resulting from a parse of the resulting quote is still attributed to
    // the site the string literal occurred, which was in a source file
    // _other_ than the one the user has control over. For example, an
    // error in a quote from the protocol compiler, invoked in user code
    // using macro_rules! for example, will be attributed to the macro_rules.rs
    // file in libsyntax, which the user might not even have source to (unless
    // they happen to have a compiler on hand). Over all, the phase distinction
    // just makes quotes "hard to attribute". Possibly this could be fixed
    // by recreating some of the original qq machinery in the tt regime
    // (pushing fake FileMaps onto the parser to account for original sites
    // of quotes, for example) but at this point it seems not likely to be
    // worth the hassle.

@kmcallister
Copy link
Contributor

I believe all of this can be fixed in a library post-1.0, albeit not as cleanly.

goffrie added a commit to goffrie/rust that referenced this issue Mar 5, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

Two new `Nonterminal`s are added: NtArm and NtMethod, which the parser
now interpolates. These are just for quasiquote. They aren't used by
macros (although they could be in the future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
goffrie added a commit to goffrie/rust that referenced this issue Mar 10, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

Two new `Nonterminal`s are added: NtArm and NtMethod, which the parser
now interpolates. These are just for quasiquote. They aren't used by
macros (although they could be in the future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
goffrie added a commit to goffrie/rust that referenced this issue Mar 13, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

Two new `Nonterminal`s are added: NtArm and NtMethod, which the parser
now interpolates. These are just for quasiquote. They aren't used by
macros (although they could be in the future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
goffrie added a commit to goffrie/rust that referenced this issue Mar 16, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

A new `Nonterminal` is added, NtArm, which the parser now interpolates.
This is just for quasiquote, not macros (although it could be in the
future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
goffrie added a commit to goffrie/rust that referenced this issue Apr 9, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

A new `Nonterminal` is added, NtArm, which the parser now interpolates.
This is just for quasiquote, not macros (although it could be in the
future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
goffrie added a commit to goffrie/rust that referenced this issue Apr 19, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

A new `Nonterminal` is added, NtArm, which the parser now interpolates.
This is just for quasiquote, not macros (although it could be in the
future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
goffrie added a commit to goffrie/rust that referenced this issue Apr 23, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

A new `Nonterminal` is added, NtArm, which the parser now interpolates.
This is just for quasiquote, not macros (although it could be in the
future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
goffrie added a commit to goffrie/rust that referenced this issue Apr 23, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

A new `Nonterminal` is added, NtArm, which the parser now interpolates.
This is just for quasiquote, not macros (although it could be in the
future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
goffrie added a commit to goffrie/rust that referenced this issue Apr 24, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

A new `Nonterminal` is added, NtArm, which the parser now interpolates.
This is just for quasiquote, not macros (although it could be in the
future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
goffrie added a commit to goffrie/rust that referenced this issue Apr 26, 2015
This changes the `ToTokens` implementations for expressions, statements,
etc. with almost-trivial ones that produce `Interpolated(*Nt(...))`
pseudo-tokens. In this way, quasiquote now works the same way as macros
do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves
pretty-printing at all, which removes the need for the
`encode_with_hygiene` hack. All associated machinery is removed.

A new `Nonterminal` is added, NtArm, which the parser now interpolates.
This is just for quasiquote, not macros (although it could be in the
future).

`ToTokens` is no longer implemented for `Arg` (although this could be
added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of
`ToTokens` to turn AST fragments back into inspectable token trees. For
this reason, this closes rust-lang#16987.

As such, this is a [breaking-change].

Fixes rust-lang#16472.
Fixes rust-lang#15962.
Fixes rust-lang#17397.
Fixes rust-lang#16617.
bors added a commit that referenced this issue Apr 26, 2015
This changes the `ToTokens` implementations for expressions, statements, etc. with almost-trivial ones that produce `Interpolated(*Nt(...))` pseudo-tokens. In this way, quasiquote now works the same way as macros do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves pretty-printing at all, which removes the need for the `encode_with_hygiene` hack. All associated machinery is removed.

New `Nonterminal`s are added: NtArm, NtImplItem, and NtTraitItem. These are just for quasiquote, not macros.

`ToTokens` is no longer implemented for `Arg` (although this could be added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of `ToTokens` to turn AST fragments back into inspectable token trees. For this reason, this closes #16987.

As such, this is a [breaking-change].

Fixes #16472.
Fixes #15962.
Fixes #17397.
Fixes #16617.
bors added a commit to rust-lang-ci/rust that referenced this issue Nov 27, 2023
fix: add fallback for completion label details

This PR adds a fallback to a previous implementation in a case when the label detail field isn't supported by LSP client and the support isn't reported by the LSP initialize request. In this case additional info about trait and aliases would be merged into the label field as it was before the rust-lang#15956 PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-syntaxext Area: Syntax extensions
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants