Skip to content

Conversation

jieyouxu
Copy link
Member

@jieyouxu jieyouxu commented Jun 7, 2023

Example

#![warn(unit_bindings)]

macro_rules! owo {
    () => {
        let whats_this = ();
    }
}

fn main() {
    // No warning if user explicitly wrote `()` on either side.
    let expr = ();
    let () = expr;
    let _ = ();

    let _ = expr; //~ WARN binding has unit type
    let pat = expr; //~ WARN binding has unit type
    let _pat = expr; //~ WARN binding has unit type

    // No warning for let bindings with unit type in macro expansions.
    owo!();

    // No warning if user explicitly annotates the unit type on the binding.
    let pat: () = expr;
}

outputs

warning: binding has unit type `()`
  --> $DIR/unit-bindings.rs:17:5
   |
LL |     let _ = expr;
   |     ^^^^-^^^^^^^^
   |         |
   |         this pattern is inferred to be the unit type `()`
   |
note: the lint level is defined here
  --> $DIR/unit-bindings.rs:3:9
   |
LL | #![warn(unit_bindings)]
   |         ^^^^^^^^^^^^^

warning: binding has unit type `()`
  --> $DIR/unit-bindings.rs:18:5
   |
LL |     let pat = expr;
   |     ^^^^---^^^^^^^^
   |         |
   |         this pattern is inferred to be the unit type `()`

warning: binding has unit type `()`
  --> $DIR/unit-bindings.rs:19:5
   |
LL |     let _pat = expr;
   |     ^^^^----^^^^^^^^
   |         |
   |         this pattern is inferred to be the unit type `()`

warning: 3 warnings emitted

This lint is not triggered if any of the following conditions are met:

  • The user explicitly annotates the binding with the () type.
  • The binding is from a macro expansion.
  • The user explicitly wrote let () = init;
  • The user explicitly wrote let pat = ();. This is allowed for local lifetimes.

Known Issue

It is known that this lint can trigger on some proc-macro generated code whose span returns false for Span::from_expansion because e.g. the proc-macro simply forwards user code spans, and otherwise don't have distinguishing syntax context compared to non-macro-generated code.

Closes #71432.

@rustbot
Copy link
Collaborator

rustbot commented Jun 7, 2023

r? @compiler-errors

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 7, 2023
@est31
Copy link
Member

est31 commented Jun 7, 2023

It's more a lint for bindings of unit type, right? Maybe it should be renamed to unit_bindings? Or are there hypothetically other instances where the lint could warn?

I'm wondering because clippy has a let and return lint, something which I am hitting often because I like to make the pattern and don't really like the lint. Still, it would be good to instantly know the difference between the lint this PR is adding and clippy::let_and_return.

@jieyouxu
Copy link
Member Author

jieyouxu commented Jun 7, 2023

It's more a lint for bindings of unit type, right? Maybe it should be renamed to unit_bindings? Or are there hypothetically other instances where the lint could warn?

Yep, I can rename it to unit_binding, it's called useless_binding now because I couldn't come up with a better name for it. I would also appreciate any feedback on the lint wording / note wording, because I don't think they're very useful (heh) right now.

@jieyouxu jieyouxu force-pushed the useless-bindings-lint branch from c374f0e to 595d1bc Compare June 7, 2023 13:51
@jieyouxu jieyouxu changed the title Add allow-by-default lint for useless bindings Add allow-by-default lint for unit bindings Jun 7, 2023
@jieyouxu jieyouxu force-pushed the useless-bindings-lint branch from 595d1bc to ceb30c6 Compare June 7, 2023 13:55
@WaffleLapkin WaffleLapkin self-assigned this Jun 8, 2023
@jieyouxu jieyouxu force-pushed the useless-bindings-lint branch from ceb30c6 to 9bde17a Compare June 9, 2023 03:09
Copy link
Member

@WaffleLapkin WaffleLapkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation looks good.

@WaffleLapkin
Copy link
Member

T-lang requested a crater run, can you add a temporary commit that makes this lint deny-by-default, so that we can make a crater run and see how much crates have triggered this?

@jieyouxu
Copy link
Member Author

jieyouxu commented Jun 12, 2023

T-lang requested a crater run, can you add a temporary commit that makes this lint deny-by-default, so that we can make a crater run and see how much crates have triggered this?

I will make a temporary commit, but I think crater supports -D unit_bindings (cf. https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Allowing.20a.20lint.20on.20diesel.20for.20bors.20try/near/328933796). Something like #107880 (comment).

EDIT: there are several UI tests I will need to fix.

@jieyouxu jieyouxu force-pushed the useless-bindings-lint branch from 9bde17a to eeadf63 Compare June 12, 2023 07:47
@rust-log-analyzer

This comment has been minimized.

@jieyouxu

This comment was marked as outdated.

@danielhenrymantilla
Copy link
Contributor

Related to this change, the following limitation:

As in, while I agree that implicit bindings of () have a rather high likelihood of being an oversight, and thus, a mistake/logic bug (which makes the lint, and this PR, very useful!), there are more rare but nonetheless legitimate cases where a () value is bound, but in a properly annotated way to acknowledge this fact (either by using the () pattern, or a : () type annotation). In such cases, this lint firing is a false-positive, and thus, an annoyance. So this is something to be aware of ⚠️

I think it will just be a matter of waiting for rust-lang/rust-clippy#10844 to be merged, and we will be good to go! 🙂

@jieyouxu
Copy link
Member Author

Opened #112549 to adjust multiple UI tests to help reduce the amount of test failures.

@jieyouxu
Copy link
Member Author

I updated the lint to not fire in the following cases:

  • The user explicitly wrote let () = init;.
  • The user explicitly wrote let pat = ();.

@rust-log-analyzer

This comment has been minimized.

@jieyouxu
Copy link
Member Author

Maybe the unit_bindings lint should also not fire when the binding is let _?

bors added a commit to rust-lang-ci/rust that referenced this pull request Jun 13, 2023
…, r=Nilstrieb

Adjust UI tests for `unit_bindings` lint

- Explicitly annotate `let x: () = expr;` where `x` has unit type, or remove the unit binding to leave only `expr;` instead.
- Use `let () = init;` or `let pat = ();` where appropriate.
- Fix disjoint-capture-in-same-closure test which wasn't actually testing a closure: `tests/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs`.

Note that unfortunately there's *a lot* of UI tests, there are a couple of places where I may have left something like `let (): ()` (this is not needed but is left over from an ealier version of the lint) which is bad style.

This PR is to help with the `unit_bindings` lint at rust-lang#112380.
@WaffleLapkin
Copy link
Member

@jieyouxu I think it makes sense to lint for let _, unsure if there are any cases where you'd want to ignore the unit like that (i.e. without asserting the type like let () = or let _: () = do)

@WaffleLapkin
Copy link
Member

I will make a temporary commit, but I think crater supports -D unit_bindings

Oh, in this case I think you can drop the commit and we can start a perf run with the flag

@jieyouxu
Copy link
Member Author

Oh, in this case I think you can drop the commit and we can start a perf run with the flag

IIUC this is going to still have to wait on #112549 and a few more UI test adjustments, since I think it won't successfully build due to the UI tests.

@jieyouxu jieyouxu force-pushed the useless-bindings-lint branch from 4ea39f2 to 0ea2618 Compare June 13, 2023 16:19
@bors
Copy link
Collaborator

bors commented Nov 19, 2023

⌛ Testing commit f0520fa with merge 16ee2f7...

bors added a commit to rust-lang-ci/rust that referenced this pull request Nov 19, 2023
…ffleLapkin

Add allow-by-default lint for unit bindings

### Example

```rust
#![warn(unit_bindings)]

macro_rules! owo {
    () => {
        let whats_this = ();
    }
}

fn main() {
    // No warning if user explicitly wrote `()` on either side.
    let expr = ();
    let () = expr;
    let _ = ();

    let _ = expr; //~ WARN binding has unit type
    let pat = expr; //~ WARN binding has unit type
    let _pat = expr; //~ WARN binding has unit type

    // No warning for let bindings with unit type in macro expansions.
    owo!();

    // No warning if user explicitly annotates the unit type on the binding.
    let pat: () = expr;
}
```

outputs

```
warning: binding has unit type `()`
  --> $DIR/unit-bindings.rs:17:5
   |
LL |     let _ = expr;
   |     ^^^^-^^^^^^^^
   |         |
   |         this pattern is inferred to be the unit type `()`
   |
note: the lint level is defined here
  --> $DIR/unit-bindings.rs:3:9
   |
LL | #![warn(unit_bindings)]
   |         ^^^^^^^^^^^^^

warning: binding has unit type `()`
  --> $DIR/unit-bindings.rs:18:5
   |
LL |     let pat = expr;
   |     ^^^^---^^^^^^^^
   |         |
   |         this pattern is inferred to be the unit type `()`

warning: binding has unit type `()`
  --> $DIR/unit-bindings.rs:19:5
   |
LL |     let _pat = expr;
   |     ^^^^----^^^^^^^^
   |         |
   |         this pattern is inferred to be the unit type `()`

warning: 3 warnings emitted
```

This lint is not triggered if any of the following conditions are met:

- The user explicitly annotates the binding with the `()` type.
- The binding is from a macro expansion.
- The user explicitly wrote `let () = init;`
- The user explicitly wrote `let pat = ();`. This is allowed for local lifetimes.

### Known Issue

It is known that this lint can trigger on some proc-macro generated code whose span returns false for `Span::from_expansion` because e.g. the proc-macro simply forwards user code spans, and otherwise don't have distinguishing syntax context compared to non-macro-generated code. For those kind of proc-macros, I believe the correct way to fix them is to instead emit identifers with span like `Span::mixed_site().located_at(user_span)`.

Closes rust-lang#71432.
@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Collaborator

bors commented Nov 19, 2023

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Nov 19, 2023
@est31
Copy link
Member

est31 commented Nov 19, 2023

the build failure is legit, likely needs a rebase plus x fmt (rustfmt now formats let chains). @rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Nov 19, 2023
This lint is not triggered if any of the following conditions are met:

- The user explicitly annotates the binding with the `()` type.
- The binding is from a macro expansion.
- The user explicitly wrote `let () = init;`
- The user explicitly wrote `let pat = ();`. This is allowed for local
  lifetimes.
@jieyouxu jieyouxu force-pushed the useless-bindings-lint branch from f0520fa to 8da09ae Compare November 20, 2023 03:47
@WaffleLapkin
Copy link
Member

@bors r+

@bors
Copy link
Collaborator

bors commented Nov 22, 2023

📌 Commit 8da09ae has been approved by WaffleLapkin

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Nov 22, 2023
@bors
Copy link
Collaborator

bors commented Nov 22, 2023

⌛ Testing commit 8da09ae with merge 73bc121...

@bors
Copy link
Collaborator

bors commented Nov 22, 2023

☀️ Test successful - checks-actions
Approved by: WaffleLapkin
Pushing 73bc121 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Nov 22, 2023
@bors bors merged commit 73bc121 into rust-lang:master Nov 22, 2023
@rustbot rustbot added this to the 1.76.0 milestone Nov 22, 2023
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (73bc121): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-1.0% [-1.0%, -1.0%] 3
All ❌✅ (primary) - - 0

Cycles

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
2.3% [2.0%, 2.5%] 3
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 2.3% [2.0%, 2.5%] 3

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 676.864s -> 675.017s (-0.27%)
Artifact size: 313.80 MiB -> 313.79 MiB (-0.00%)

wip-sync pushed a commit to NetBSD/pkgsrc-wip that referenced this pull request Feb 18, 2024
Pkgsrc changes:
 * Adapt checksums and patches.

Upstream chnages:

Version 1.76.0 (2024-02-08)
==========================

Language
--------
- [Document Rust ABI compatibility between various types]
  (rust-lang/rust#115476)
- [Also: guarantee that char and u32 are ABI-compatible]
  (rust-lang/rust#118032)
- [Warn against ambiguous wide pointer comparisons]
  (rust-lang/rust#117758)

Compiler
--------
- [Lint pinned `#[must_use]` pointers (in particular, `Box<T>`
  where `T` is `#[must_use]`) in `unused_must_use`.]
  (rust-lang/rust#118054)
- [Soundness fix: fix computing the offset of an unsized field in
  a packed struct]
  (rust-lang/rust#118540)
- [Soundness fix: fix dynamic size/align computation logic for
  packed types with dyn Trait tail]
  (rust-lang/rust#118538)
- [Add `$message_type` field to distinguish json diagnostic outputs]
  (rust-lang/rust#115691)
- [Enable Rust to use the EHCont security feature of Windows]
  (rust-lang/rust#118013)
- [Add tier 3 {x86_64,i686}-win7-windows-msvc targets]
  (rust-lang/rust#118150)
- [Add tier 3 aarch64-apple-watchos target]
  (rust-lang/rust#119074)
- [Add tier 3 arm64e-apple-ios & arm64e-apple-darwin targets]
  (rust-lang/rust#115526)

Refer to Rust's [platform support page][platform-support-doc]
for more information on Rust's tiered platform support.

Libraries
---------
- [Add a column number to `dbg!()`]
  (rust-lang/rust#114962)
- [Add `std::hash::{DefaultHasher, RandomState}` exports]
  (rust-lang/rust#115694)
- [Fix rounding issue with exponents in fmt]
  (rust-lang/rust#116301)
- [Add T: ?Sized to `RwLockReadGuard` and `RwLockWriteGuard`'s Debug impls.]
  (rust-lang/rust#117138)
- [Windows: Allow `File::create` to work on hidden files]
  (rust-lang/rust#116438)

Stabilized APIs
---------------
- [`Arc::unwrap_or_clone`]
  (https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.unwrap_or_clone)
- [`Rc::unwrap_or_clone`]
  (https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.unwrap_or_clone)
- [`Result::inspect`]
  (https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect)
- [`Result::inspect_err`]
  (https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect_err)
- [`Option::inspect`]
  (https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.inspect)
- [`type_name_of_val`]
  (https://doc.rust-lang.org/stable/std/any/fn.type_name_of_val.html)
- [`std::hash::{DefaultHasher, RandomState}`]
  (https://doc.rust-lang.org/stable/std/hash/index.html#structs)
  These were previously available only through `std::collections::hash_map`.
- [`ptr::{from_ref, from_mut}`]
  (https://doc.rust-lang.org/stable/std/ptr/fn.from_ref.html)
- [`ptr::addr_eq`](https://doc.rust-lang.org/stable/std/ptr/fn.addr_eq.html)

Cargo
-----

See [Cargo release notes]
(https://github.com/rust-lang/cargo/blob/master/CHANGELOG.md#cargo-176-2024-02-08).

Rustdoc
-------
- [Don't merge cfg and doc(cfg) attributes for re-exports]
  (rust-lang/rust#113091)
- [rustdoc: allow resizing the sidebar / hiding the top bar]
  (rust-lang/rust#115660)
- [rustdoc-search: add support for traits and associated types]
  (rust-lang/rust#116085)
- [rustdoc: Add highlighting for comments in items declaration]
  (rust-lang/rust#117869)

Compatibility Notes
-------------------
- [Add allow-by-default lint for unit bindings]
  (rust-lang/rust#112380)
  This is expected to be upgraded to a warning by default in a future Rust
  release. Some macros emit bindings with type `()` with user-provided spans,
  which means that this lint will warn for user code.
- [Remove x86_64-sun-solaris target.]
  (rust-lang/rust#118091)
- [Remove asmjs-unknown-emscripten target]
  (rust-lang/rust#117338)
- [Report errors in jobserver inherited through environment variables]
  (rust-lang/rust#113730)
  This [may warn](rust-lang/rust#120515)
  on benign problems too.
- [Update the minimum external LLVM to 16.]
  (rust-lang/rust#117947)
- [Improve `print_tts`](rust-lang/rust#114571)
  This change can break some naive manual parsing of token trees
  in proc macro code which expect a particular structure after
  `.to_string()`, rather than just arbitrary Rust code.
- [Make `IMPLIED_BOUNDS_ENTAILMENT` into a hard error from a lint]
  (rust-lang/rust#117984)
- [Vec's allocation behavior was changed when collecting some iterators]
  (rust-lang/rust#110353)
  Allocation behavior is currently not specified, nevertheless
  changes can be surprising.
  See [`impl FromIterator for Vec`]
  (https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#impl-FromIterator%3CT%3E-for-Vec%3CT%3E)
  for more details.
- [Properly reject `default` on free const items]
  (rust-lang/rust#117818)
@jieyouxu jieyouxu deleted the useless-bindings-lint branch March 15, 2024 14:45
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Oct 21, 2024
…pkin

Improve test coverage for `unit_bindings` lint

Follow-up to rust-lang#112380, apparently at the time I didn't add much of any test coverage outside of just "generally works as intended on the test suites and in the crater run".

r? compiler
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Oct 21, 2024
Rollup merge of rust-lang#131999 - jieyouxu:unit-bindings, r=WaffleLapkin

Improve test coverage for `unit_bindings` lint

Follow-up to rust-lang#112380, apparently at the time I didn't add much of any test coverage outside of just "generally works as intended on the test suites and in the crater run".

r? compiler
@jieyouxu jieyouxu added the L-unit_bindings Lint: unit_bindings label Feb 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. L-unit_bindings Lint: unit_bindings merged-by-bors This PR was explicitly merged by bors. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Warn on useless bindings like let v2 = v1.sort();