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

Strange formatting with nested macro_rules! and long comment #4325

Closed
Aaron1011 opened this issue Jul 14, 2020 · 1 comment · Fixed by #4339
Closed

Strange formatting with nested macro_rules! and long comment #4325

Aaron1011 opened this issue Jul 14, 2020 · 1 comment · Fixed by #4339
Assignees
Labels
1x-backport:pending Fixed/resolved in source but not yet backported to a 1x branch and release a-comments a-macros poor-formatting

Comments

@Aaron1011
Copy link
Member

Input

macro_rules! bad {
    () => {
        macro_rules! inner {
            () => {
                // This needs to have a width of over 100 characters to trigger the issue 12345678901
                ("a", "B")
            };
        }
    };
}

Output

macro_rules! bad {
    () => {
        macro_rules! inner {
                    () => {
                        // This needs to have a width of over 100 characters to trigger the issue 12345678901
                        ("a", "B")
                    };
                }
    };
}

Expected output

macro_rules! bad {
    () => {
        macro_rules! inner {
            () => {
                // This needs to have a width of over 100 characters to trigger the issue 12345678901
                ("a", "B")
            };
        }
    };
}

Meta

  • rustfmt version: rustfmt 1.4.16-stable (939e164 2020-06-11)
  • From where did you install rustfmt?: rustup

Modifying the comment to be less than 100 characters causes rustfmt to leave the code unchanged.

Aaron1011 added a commit to Aaron1011/solana that referenced this issue Jul 14, 2020
Due to rust-lang/rustfmt#4325, leaving this as
one line causes rustfmt to add extra indentation to the surrounding
code.
mvines pushed a commit to solana-labs/solana that referenced this issue Jul 14, 2020
Due to rust-lang/rustfmt#4325, leaving this as
one line causes rustfmt to add extra indentation to the surrounding
code.
@ayazhafiz
Copy link
Contributor

The problem is that we bail on trying to format the inner code block if any formatted line comes out to >100 chars:

} else if line.len() > config.max_width() {
// If there are lines that are larger than max width, we cannot tell
// whether we have succeeded but have some comments or strings that
// are too long, or we have failed to format code block. We will be
// conservative and just return `None` in this case.
return None;

I'm not convinced this check is needed since we jump out early if retrieving the formatted snippet fails:

let mut formatted = format_snippet(&snippet, &config_with_unix_newline)?;

And no system/idempotency tests are broken by removing this. In the most conservative case I think it is okay to make an exemption for macros here, since we can't do better anyway.

mergify bot pushed a commit to solana-labs/solana that referenced this issue Jul 15, 2020
Due to rust-lang/rustfmt#4325, leaving this as
one line causes rustfmt to add extra indentation to the surrounding
code.

(cherry picked from commit fed69e9)
mergify bot added a commit to solana-labs/solana that referenced this issue Jul 15, 2020
…0905) (#11073)

* Fix hygiene issues in `declare_program!` and `declare_loader!`

The `declare_program!` and `declare_loader!` macros both expand to
new macro definitions (based on the `$name` argument). These 'inner'
macros make use of the special `$crate` metavariable to access items in
the crate where the 'inner' macros is defined.

However, this only works due to a bug in rustc. When a macro is
expanded, all `$crate` tokens in its output are 'marked' as being
resolved in the defining crate of that macro. An inner macro (including
the body of its arms) is 'just' another set of tokens that appears in
the body of the outer macro, so any `$crate` identifiers used there are
resolved relative to the 'outer' macro.

For example, consider the following code:

```rust
macro_rules! outer {
    () => {
        macro_rules! inner {
            () => {
                $crate::Foo
            }
        }
    }
}
```

The path `$crate::Foo` will be resolved relative to the crate that defines `outer`,
**not** the crate which defines `inner`.

However, rustc currently loses this extra resolution information
(referred to as 'hygiene' information) when a crate is serialized.
In the above example, this means that the macro `inner` (which gets
defined in whatever crate invokes `outer!`) will behave differently
depending on which crate it is invoked from:

When `inner` is invoked from the same crate in which it is defined,
the hygiene information will still be available,
which will cause `$crate::Foo` to be resolved in the crate which defines 'outer'.

When `inner` is invoked from a different crate, it will be loaded from
the metadata of the crate which defines 'inner'. Since the hygiene
information is currently lost, rust will 'forget' that `$crate::Foo` is
supposed to be resolved in the context of 'outer'. Instead, it will be
resolved relative to the crate which defines 'inner', which can cause
incorrect code to compile.

This bug will soon be fixed in rust (see rust-lang/rust#72121),
which will break `declare_program!` and `declare_loader!`. Fortunately,
it's possible to obtain the desired behavior (`$crate` resolving in the
context of the 'inner' macro) by use of a procedural macro.

This commit adds a `respan!` proc-macro to the `sdk/macro` crate.
Using the newly-stabilized (on Nightly) `Span::resolved_at` method,
the `$crate` identifier can be made to be resolved in the context of the
proper crate.

Since `Span::resolved_at` is only stable on the latest nightly,
referencing it on an earlier version of Rust will cause a compilation error.
This requires the `rustversion` crate to be used, which allows conditionally
compiling code epending on the Rust compiler version in use. Since this method is already
stabilized in the latest nightly, there will never be a situation where
the hygiene bug is fixed (e.g. rust-lang/rust#72121)
is merged but we are unable to call `Span::resolved_at`.

(cherry picked from commit 05445c7)

# Conflicts:
#	Cargo.lock
#	sdk/Cargo.toml

* Replace FIXME with an issue link

(cherry picked from commit b0cb2b0)

* Update lock files

(cherry picked from commit 42f8848)

# Conflicts:
#	programs/bpf/Cargo.lock
#	programs/librapay/Cargo.lock
#	programs/move_loader/Cargo.lock

* Split comment over multiple lines

Due to rust-lang/rustfmt#4325, leaving this as
one line causes rustfmt to add extra indentation to the surrounding
code.

(cherry picked from commit fed69e9)

* Fix clippy lints

(cherry picked from commit e7387f6)

* Apply #![feature(proc_macro_hygiene)] when needed

This allows the rust-bpf-builder toolchain to build the sdk

(cherry picked from commit 95490ff)

# Conflicts:
#	sdk/build.rs
#	sdk/src/lib.rs

* Update Cargo.toml

* Update lib.rs

* Add rustc_version

* lock file updates

Co-authored-by: Aaron Hill <aa1ronham@gmail.com>
Co-authored-by: Jack May <jack@solana.com>
Co-authored-by: Michael Vines <mvines@gmail.com>
ayazhafiz added a commit to ayazhafiz/rustfmt that referenced this issue Jul 21, 2020
Today rustfmt bails when trying to handle a code block that, after
formatting, has lines >100 chars. The reasoning was that if there were
lines longer than 100 chars it wasn't clear if we failed to format or
that was the intended result, so to be cautious the handler would bail.

However, if formatting the snippet fails the code block formatter will
already have jumped out by the time this line-width check is done:

https://github.com/rust-lang/rustfmt/blob/ff57cc6293ce450380dbb6da6a1a3656dbae538d/src/formatting/util.rs#L81

So I am not sure that this check is needed, and removing it broke no
tests except one designed explicitly for it. NB w.r.t. that unit test,
formatting a code like

```
fn main() {
    let expected = "this_line_is_100_characters_long_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(x, y, z);";
}
```

would yield no change, so I believe it is also the expected result.

Closes rust-lang#4325
@calebcartwright calebcartwright added the 1x-backport:pending Fixed/resolved in source but not yet backported to a 1x branch and release label Jan 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1x-backport:pending Fixed/resolved in source but not yet backported to a 1x branch and release a-comments a-macros poor-formatting
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants