Can't get Clippy to allow push after Vec:::new

Clippy error:

warning: calls to `push` immediately after creation
   --> sharpview/src/main.rs:561:5
    |
561 | /     let mut grids = Vec::new();
562 | |     //  Replay feature only if replay enabled
563 | |     #[cfg (feature="replay")]
564 | |     #[allow(clippy::vec_init_then_push)]    // Clippy doesn't understand the conditional compilation here.
...   |
583 | |         login_url: Some("http://login.osgrid.org".to_string()),
584 | |     });
    | |_______^ help: consider using the `vec![]` macro: `let mut grids = vec![..];`
    |
    = note: `#[warn(clippy::vec_init_then_push)]` on by default
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master

Here, Clippy is complaining about creating an empty vector, followed by a "push". But the first push is inside a conditionally compiled code block, so it might be turned off.

the problem is that I can't get Clippy to shut up about this by adding

#[allow(clippy::vec_init_then_push)] 

at several places. Here's the actual code:

fn get_grid_select_params(assets: &GuiAssets) -> Vec<GridSelectParams> {
    #[allow(clippy::vec_init_then_push)]    // Clippy doesn't understand the conditional compilation here.
    let mut grids = Vec::new();
    //  Replay feature only if replay enabled
    #[cfg (feature="replay")]
    #[allow(clippy::vec_init_then_push)]    // Clippy doesn't understand the conditional compilation here.
    grids.push(GridSelectParams {
        name: "Replay file".to_string(),
        picture_bar: assets.replay_bar,
        web_url: "https://www.animats.com/viewer".to_string(),
        login_url: None,
    });
    #[allow(clippy::vec_init_then_push)]    // Clippy doesn't understand the conditional compilation here.
    grids.push(GridSelectParams {
        name: "Second Life".to_string(),
        picture_bar: assets.placeholder_a_bar,
        web_url: "https://www.secondlife.com".to_string(),
        login_url: Some("https://login.aditi.lindenlab.com/cgi-bin/login.cgi".to_string()),
    });
    #[allow(clippy::vec_init_then_push)]    // Clippy doesn't understand the conditional compilation here.
    grids.push(GridSelectParams {
        name: "OsGrid".to_string(),
        picture_bar: assets.placeholder_b_bar,
        web_url: "https://www.osgrid.org".to_string(),
        login_url: Some("http://login.osgrid.org".to_string()),
    });
\
    grids
}

Why won't Clippy accept that "allow"?
Incidentally, Rust Playground's "clippy" seems to be happy with push after new in simple cases.

Have you tried applying it to the function scope...

#[allow(clippy::vec_init_then_push)]
fn get_grid_select_params(assets: &GuiAssets) -> Vec<GridSelectParams> {
3 Likes

That does appear to work

You can actually make vec! work too I think, though I haven't tested that too much

let grids = vec![
    //  Replay feature only if replay enabled
    #[cfg(feature = "replay")]
    GridSelectParams {
        name: "Replay file".to_string(),
        picture_bar: assets.replay_bar,
        web_url: "https://www.animats.com/viewer".to_string(),
        login_url: None,
    },
    GridSelectParams {
        name: "Second Life".to_string(),
        picture_bar: assets.placeholder_a_bar,
        web_url: "https://www.secondlife.com".to_string(),
        login_url: Some("https://login.aditi.lindenlab.com/cgi-bin/login.cgi".to_string()),
    },
    GridSelectParams {
        name: "OsGrid".to_string(),
        picture_bar: assets.placeholder_b_bar,
        web_url: "https://www.osgrid.org".to_string(),
        login_url: Some("http://login.osgrid.org".to_string()),
    },
];
2 Likes

That works. I try to use those "allow" instructions at statement scope, but maybe that doesn't work on lints that involve two separate statements.

I have misgivings about using #cfg in the middle of an expression. The Rust Reference says: "The cfg attribute conditionally includes the thing it is attached to based on a configuration predicate."

The "thing it is attached to" is rather vague. "Attribute" is defined as " Outer attributes, written without the bang after the hash, apply to the thing that follows the attribute." "Thing" does not seem to be defined anywhere.

So, #cfg, in the middle of an expression, might be considered using undefined behavior.

I suppose Clippy only checks that (all?) lint's trigger at block scope. This works:

fn get_grid_select_params(assets: &GuiAssets) -> Vec<GridSelectParams> {
    let mut grids = Vec::new();
    //  Replay feature only if replay enabled
    #[cfg(feature = "replay")]
    grids.push(GridSelectParams {
        name: "Replay file".to_string(),
        picture_bar: assets.replay_bar,
        web_url: "https://www.animats.com/viewer".to_string(),
        login_url: None,
    });
    #[allow(clippy::vec_init_then_push)]
    {
        grids.push(GridSelectParams {
            name: "Second Life".to_string(),
            picture_bar: assets.placeholder_a_bar,
            web_url: "https://www.secondlife.com".to_string(),
            login_url: Some("https://login.aditi.lindenlab.com/cgi-bin/login.cgi".to_string()),
        });
        grids.push(GridSelectParams {
            name: "OsGrid".to_string(),
            picture_bar: assets.placeholder_b_bar,
            web_url: "https://www.osgrid.org".to_string(),
            login_url: Some("http://login.osgrid.org".to_string()),
        });
    }
    grids
}

An attribute can be attached to an expression, statement, or item, and extends to cover just that syntax node. Anywhere an attribute is allowed it's fully unambiguous which is being decorated.

Rest assured that this is covered under the stability guarantee, even if it's undocumented at the moment.

2 Likes