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

Wrong "unnecessary parentheses around function argument"-warning #47775

Closed
SWW13 opened this issue Jan 26, 2018 · 8 comments
Closed

Wrong "unnecessary parentheses around function argument"-warning #47775

SWW13 opened this issue Jan 26, 2018 · 8 comments
Labels
A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. P-high High priority regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@SWW13
Copy link

SWW13 commented Jan 26, 2018

Recent nightly versions give warnings when using the macro do_parse!() of nom:

I tried this code:

named!(pub parse_string <String>, do_parse!(
    value: map_res!(take_until!("\x00"), |value: &[u8]| String::from_utf8(value.to_vec())) >>
    tag!(b"\x00") >>
    (value)
));

I expected to see this happen:
No warning.

Instead, this happened:

warning: unnecessary parentheses around function argument
   |
13 | / named!(pub parse_string <String>, do_parse!(
14 | |     value: map_res!(take_until!("\x00"), |value: &[u8]| String::from_utf8(value.to_vec())) >>
15 | |     tag!(b"\x00") >>
16 | |     (value)
17 | | ));
   | |___^ help: remove these parentheses
   |
   = note: #[warn(unused_parens)] on by default
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

After investigating this further I found the source of the warning (see SWW13/nom@f1a04eb#diff-06e70f2a187023418eedf26d22d3aa5d).
But these parentheses are needed for the additional tuple, removing them breaks the nom tests.

CI logs show last working version rustc 1.25.0-nightly (da569fa9d 2018-01-16) and first broken version rustc 1.25.0-nightly (97520ccb1 2018-01-21).

Meta

Related Issue: rust-bakery/nom#668

rustc --version --verbose:

rustc 1.25.0-nightly (9fd7da904 2018-01-25)
binary: rustc
commit-hash: 9fd7da904b46ff7aa78c2e2cc1986c4975aeccc6
commit-date: 2018-01-25
host: x86_64-unknown-linux-gnu
release: 1.25.0-nightly
LLVM version: 4.0
@nagisa nagisa added A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. labels Jan 26, 2018
@zackmdavis
Copy link
Member

Introduced in #46980. 😰

I'm having trouble fully understanding the nom example, but prior work suggests it's possible to not lint macro-generated code, which I think is reasonable in this case.

@zackmdavis
Copy link
Member

I haven't been able to construct a minimal standalone example (which we would want in order to attempt a more fundamental fix before resorting to the lazy answer of just not linting macro expansions). The macro backtrace from nom is somewhat opaque:

zmd@ReflectiveCoherence:~/Code/Misc/repro_paren/src$ RUSTFLAGS="-Z unstable-options -Z external-macro-backtrace -F warnings" cargo +nightly run 
   Compiling libc v0.2.36
   Compiling memchr v1.0.2
   Compiling nom v3.2.1
   Compiling repro_paren v0.1.0 (file:///home/zmd/Code/Misc/repro_paren)
error: unnecessary parentheses around function argument
   --> <do_parse macros>:2:36
    |
1   |         ( __impl $ i : expr , $ consumed : expr , ( $ ( $ rest : expr ) , * ) ) => (
    |       __-
    |      |__|
    |     ||__|
    |    |||__|
    |   ||||
2   |   ||||  $ crate :: IResult :: Done ( $ i , ( $ ( $ rest ) , * ) ) ) ; (
    |   ||||                                     ^^^^^^^^^^^^^^^^^^^^ help: remove these parentheses
3   |   ||||  __impl $ i : expr , $ consumed : expr , $ field : ident : $ submac : ident ! (
4   |   ||||  $ ( $ args : tt ) * ) ) => (
...     ||||
49  |   ||||  let i_ = i . clone (  ) ; do_parse ! (
    |  _||||____________________________-
50  | | ||||  __impl i_ , $ consumed + (
51  | | ||||  $ crate :: InputLength :: input_len ( & ( $ i ) ) - $ crate :: InputLength ::
52  | | ||||  input_len ( & i ) ) , $ ( $ rest ) * ) } , } } ) ; (
    | |_||||_______________________________________- in this macro invocation (#5)
...     ||||
71  |   ||||  let $ field = o ; let i_ = i . clone (  ) ; do_parse ! (
    |  _||||______________________________________________-
72  | | ||||  __impl i_ , $ consumed + (
73  | | ||||  $ crate :: InputLength :: input_len ( & ( $ i ) ) - $ crate :: InputLength ::
74  | | ||||  input_len ( & i ) ) , $ ( $ rest ) * ) } , } } ) ; (
    | |_||||_______________________________________- in this macro invocation (#4)
...     ||||
112 |   ||||  { do_parse ! ( __impl $ i , 0usize , $ ( $ rest ) * ) } ) ; (
    |   ||||    --------------------------------------------------- in this macro invocation (#3)
...     ||||
123 |   ||||  ) ; ) ; ( $ e : ident ! >> $ ( $ rest : tt ) * ) => (
124 |   ||||  do_parse ! ( call ! ( $ e ) >> $ ( $ rest ) * ) ; ) ;
    |   ||||                                                      -
    |   ||||______________________________________________________|
    |   |||_______________________________________________________in this expansion of `do_parse!` (#2)
    |   ||________________________________________________________in this expansion of `do_parse!` (#3)
    |   |_________________________________________________________in this expansion of `do_parse!` (#4)
    |                                                             in this expansion of `do_parse!` (#5)
    | 
   ::: <named macros>
    |
1   |       / ( # $ ( $ args : tt ) * ) => ( named_attr ! ( # $ ( $ args ) * ) ; ) ; (
2   |       | $ name : ident ( $ i : ty ) -> $ o : ty , $ submac : ident ! (
3   |       | $ ( $ args : tt ) * ) ) => (
4   |       | # [ allow ( unused_variables ) ] fn $ name ( i : $ i ) -> $ crate :: IResult <
...         |
41  |       | crate :: IResult < & [ u8 ] , & [ u8 ] , u32 > {
42  |       | $ submac ! ( i , $ ( $ args ) * ) } ) ;
    |       |_______________________________________- in this expansion of `named!` (#1)
    | 
   ::: src/main.rs
    |
4   |             named!(pub parse_string <String>, do_parse!(
    |  ___________-_________________________________-
    | | __________|
    | ||
5   | ||              value: map_res!(take_until!("\x00"), |value: &[u8]| String::from_utf8(value.to_vec())) >>
6   | ||                  tag!(b"\x00") >>
7   | ||                  (value)
8   | ||          ));
    | ||____________- in this macro invocation (#1)
...   |
    |
    = note: `-F unused-parens` implied by `-F warnings`

error: aborting due to previous error

error: Could not compile `repro_paren`.

To learn more, run the command again with --verbose.

@LegNeato
Copy link
Contributor

LegNeato commented Jan 30, 2018

I believe we are seeing this in juniper with this PR: graphql-rust/juniper#135 (specifically graphql-rust/juniper@438d56a#diff-46914ac7413539fd200e08e90af18e09). Putting the parens in causes the warning, taking them out causes a build failure.

@zackmdavis
Copy link
Member

@LegNeato thanks. Minimization of the Juniper example:

macro_rules! name_maybe_fn_from_ident {
    ( @as_expr, $e:expr) => { $e };
    ( @generate_fn, $name:tt) => {
        fn name_maybe<'a>() -> Option<&'a str> {
            Some(name_maybe_fn_from_ident!( @as_expr, $name ))
        }
    };
    ( $name:ident ) => { name_maybe_fn_from_ident!( @generate_fn, (stringify!($name))); }
}

name_maybe_fn_from_ident!(rah);

fn main() {}

Here, the lint is "legitimately" firing when one macro expansion's single-element token-tree gets interpreted as another macro expansion's expression. (The generated code here is fn name_maybe<'a>() -> Option<&'a str> { Some(("rah")) }, with extra parens around the Some constructor's argument.)

Hopefully there's some way for the compiler to not lint this case, which macro writers shouldn't need to care about, while still linting literal unused parens in macro expansions? If not, not-linting unused parens in macro expansions at all, while an unprincipled hack, is better for users than endorsing nightly's current behavior towards cases like those of Nom and Juniper.

zackmdavis added a commit to zackmdavis/rust that referenced this issue Jan 31, 2018
In rust-lang#46980 ("in which the unused-parens lint..." (14982db)), the
unused-parens lint was made to check function and method arguments,
which it previously did not (seemingly due to oversight rather than
willful design). However, in rust-lang#47775 and discussion thereon,
user–developers of Geal/nom and graphql-rust/juniper reported that the
lint was seemingly erroneously triggering on certain complex macros in
those projects. While this doesn't seem like a bug in the lint in the
particular strict sense that the expanded code would, in fact, contain
unncecessary parentheses, it also doesn't seem like the sort of thing
macro authors should have to think about: the spirit of the
unused-parens lint is to prevent needless clutter in code, not to give
macro authors extra heartache in the handling of token trees.

We propose the expediency of declining to lint unused parentheses in
function or method args inside of nested expansions: we believe that
this should eliminate the petty, troublesome lint warnings reported
in the issue, without forgoing the benefits of the lint in simpler
macros.

It seemed like too much duplicated code for the `Call` and `MethodCall`
match arms to duplicate the nested-macro check in addition to each
having their own `for` loop, so this occasioned a slight refactor so
that the function and method cases could share code—hopefully the
overall intent is at least no less clear to the gentle reader.

This is concerning rust-lang#47775.
@zackmdavis
Copy link
Member

not-linting unused parens in macro expansions at all

Or less bad, nested macro expansions specifically: #47896

@nikomatsakis
Copy link
Contributor

triage: P-high

The behavior of the compiler while technically correct is super annoying. Thanks @zackmdavis for taking initiative.

@rust-highfive rust-highfive added the P-high High priority label Feb 1, 2018
kennytm added a commit to kennytm/rust that referenced this issue Feb 3, 2018
…ssary_unnecessary_parens, r=nikomatsakis

decline to lint technically-unnecessary parens in function or method arguments inside of nested macros

In rust-lang#46980 ("in which the unused-parens lint..." (14982db)), the
unused-parens lint was made to check function and method arguments,
which it previously did not (seemingly due to oversight rather than
willful design). However, in rust-lang#47775 and discussion thereon,
user–developers of Geal/nom and graphql-rust/juniper reported that the
lint was seemingly erroneously triggering on certain complex macros in
those projects. While this doesn't seem like a bug in the lint in the
particular strict sense that the expanded code would, in fact, contain
unncecessary parentheses, it also doesn't seem like the sort of thing
macro authors should have to think about: the spirit of the
unused-parens lint is to prevent needless clutter in code, not to give
macro authors extra heartache in the handling of token trees.

We propose the expediency of declining to lint unused parentheses in
function or method args inside of nested expansions: we believe that
this should eliminate the petty, troublesome lint warnings reported
in the issue, without forgoing the benefits of the lint in simpler
macros.

It seemed like too much duplicated code for the `Call` and `MethodCall`
match arms to duplicate the nested-macro check in addition to each
having their own `for` loop, so this occasioned a slight refactor so
that the function and method cases could share code—hopefully the
overall intent is at least no less clear to the gentle reader.

This is concerning rust-lang#47775.
kennytm added a commit to kennytm/rust that referenced this issue Feb 4, 2018
…ssary_unnecessary_parens, r=nikomatsakis

decline to lint technically-unnecessary parens in function or method arguments inside of nested macros

In rust-lang#46980 ("in which the unused-parens lint..." (14982db)), the
unused-parens lint was made to check function and method arguments,
which it previously did not (seemingly due to oversight rather than
willful design). However, in rust-lang#47775 and discussion thereon,
user–developers of Geal/nom and graphql-rust/juniper reported that the
lint was seemingly erroneously triggering on certain complex macros in
those projects. While this doesn't seem like a bug in the lint in the
particular strict sense that the expanded code would, in fact, contain
unncecessary parentheses, it also doesn't seem like the sort of thing
macro authors should have to think about: the spirit of the
unused-parens lint is to prevent needless clutter in code, not to give
macro authors extra heartache in the handling of token trees.

We propose the expediency of declining to lint unused parentheses in
function or method args inside of nested expansions: we believe that
this should eliminate the petty, troublesome lint warnings reported
in the issue, without forgoing the benefits of the lint in simpler
macros.

It seemed like too much duplicated code for the `Call` and `MethodCall`
match arms to duplicate the nested-macro check in addition to each
having their own `for` loop, so this occasioned a slight refactor so
that the function and method cases could share code—hopefully the
overall intent is at least no less clear to the gentle reader.

This is concerning rust-lang#47775.
kennytm added a commit to kennytm/rust that referenced this issue Feb 4, 2018
…ssary_unnecessary_parens, r=nikomatsakis

decline to lint technically-unnecessary parens in function or method arguments inside of nested macros

In rust-lang#46980 ("in which the unused-parens lint..." (14982db)), the
unused-parens lint was made to check function and method arguments,
which it previously did not (seemingly due to oversight rather than
willful design). However, in rust-lang#47775 and discussion thereon,
user–developers of Geal/nom and graphql-rust/juniper reported that the
lint was seemingly erroneously triggering on certain complex macros in
those projects. While this doesn't seem like a bug in the lint in the
particular strict sense that the expanded code would, in fact, contain
unncecessary parentheses, it also doesn't seem like the sort of thing
macro authors should have to think about: the spirit of the
unused-parens lint is to prevent needless clutter in code, not to give
macro authors extra heartache in the handling of token trees.

We propose the expediency of declining to lint unused parentheses in
function or method args inside of nested expansions: we believe that
this should eliminate the petty, troublesome lint warnings reported
in the issue, without forgoing the benefits of the lint in simpler
macros.

It seemed like too much duplicated code for the `Call` and `MethodCall`
match arms to duplicate the nested-macro check in addition to each
having their own `for` loop, so this occasioned a slight refactor so
that the function and method cases could share code—hopefully the
overall intent is at least no less clear to the gentle reader.

This is concerning rust-lang#47775.
@nikomatsakis nikomatsakis added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Feb 15, 2018
@nikomatsakis
Copy link
Contributor

I believe this is now fixed, right? cc @zackmdavis

@zackmdavis
Copy link
Member

@nikomatsakis yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. P-high High priority regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

6 participants