Strange clippy::shadow_unrelated error

I am getting strange clippy::shadow_unrelated errors in some of my code that I do not understand. I have produced a much simplified example and I am still unable to understand what it is complaining about:

error: `var2` shadows a previous, unrelated binding
 --> src/main.rs:3:28
  |
3 | let var1; let var2; (var1, var2) = (1, 2);
  |                            ^^^^
  |
note: previous binding is here
 ---> src/main.rs:3:22
  |
3 | let var1; let var2; (var1, var2) = (1, 2);
  |                      ^^^^
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
note: the lint level is defined here
 --> src/main.rs:1:9
  |
1 | #![deny(clippy::shadow_unrelated)]
  | 

Can someone please clarify

please provide the full source code, this lint is non local, it means you have other variables with the same name in scope, but without the full code, it's hard to figure out.

1 Like

Actually, not really.

#![deny(clippy::shadow_unrelated)]
fn main() {
    let var1;
    let var2;
    (var1, var2) = (1, 2);
    let _ = (var1, var2);
}
error: `var2` shadows a previous, unrelated binding
 --> src/main.rs:5:12
  |
5 |     (var1, var2) = (1, 2);
  |            ^^^^
  |
note: previous binding is here
 --> src/main.rs:5:6
  |
5 |     (var1, var2) = (1, 2);
  |      ^^^^
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
note: the lint level is defined here
 --> src/main.rs:1:9
  |
1 | #![deny(clippy::shadow_unrelated)]
  |         ^^^^^^^^^^^^^^^^^^^^^^^^

Seems like a bug. Possibly the lint doesn't handle destructuring assignment, which is newer.

3 Likes

wow, was not expecting that. it definitely seems to me an bug in the linter.

full code as per @vague but on fewer lines

as a newbie i didnt want to jump to conclusions but thought it might be

But it seems to accord with the design


shadow_unrelated ΒΆ :clipboard:

restriction allow βˆ’

What it does

Checks for bindings that shadow other bindings already in scope, either without an initialization or with one that does not even use the original value.

Why is this bad?

Name shadowing can hurt readability, especially in large code bases, because it is easy to lose track of the active binding at any place in the code. This can be alleviated by either giving more specific names to bindings or introducing more scopes to contain the bindings.


So for the without an initialization condition, it seems to be the case in OP.

But where was the first binding in OP before the destructuring?
I came to realize the error actually says that previous binding happens on the destructuring, i.e. on the same line!

(var1, var2) = (1, 2);
// `var2` shadows a previous, unrelated binding, and the previous binding is on var1

I don't know the real implementation behind the hint, but based on MIR Rust Playground

fn main() -> () {
    let mut _0: ();                      // return place in scope 0 at src/main.rs:2:11: 2:11
    let mut _3: (i32, i32);              // in scope 0 at src/main.rs:5:20: 5:26
    scope 1 {
        debug var1 => _1;                // in scope 1 at src/main.rs:3:9: 3:13
        scope 2 {
            debug var2 => _2;            // in scope 2 at src/main.rs:4:9: 4:13
            let _1: i32;                 // in scope 2 at src/main.rs:5:6: 5:10
            let _2: i32;                 // in scope 2 at src/main.rs:5:12: 5:16
            scope 3 {
                debug lhs => _1;         // in scope 3 at src/main.rs:5:6: 5:10
                debug lhs => _2;         // in scope 3 at src/main.rs:5:12: 5:16
            }
            scope 4 {
            }
        }
    }

    bb0: {
        _3 = (const 1_i32, const 2_i32); // scope 2 at src/main.rs:5:20: 5:26
        _1 = (_3.0: i32);                // scope 2 at src/main.rs:5:6: 5:10
        _2 = (_3.1: i32);                // scope 2 at src/main.rs:5:12: 5:16
        return;                          // scope 0 at src/main.rs:7:2: 7:2
    }
}

the symbol lhs is indeed binded twice...

1 Like

To my eye, this is clearly a bug, because

  • assignment is not shadowing, and
  • it claims that var2 is shadowing var1, but this is impossible since they are different names.
3 Likes

Ah, already reported:

Seems to use HIR in the source code: https://github.com/rust-lang/rust-clippy/blob/3be3fb7231677e043317ef026989584ba9a23e37/clippy_lints/src/shadow.rs

fn main() {
        let var1;
        let var2;
        { let (lhs, lhs) = (1, 2); var1 = lhs; var2 = lhs; };
        let _ = (var1, var2);
    }

Now it's clear.

Sorry it is still not clear to me as not familiar with HIR. What is lhs and how is it possible to use it in the HIR as some kind of FIFO? Is the bug in the lowering used by destructuring assignment or ignorance in the lint of the special nature of lhs or something else?

I haven't dug deep into them yet :frowning:

2909-destructuring-assignment - The Rust RFC Book (rust-lang.github.io) makes no mention of lhs and claims the desugaring is to

{ let (_var1, _var2) = (1, 2); var1 = _var1; var2 = _var2; }