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

Add allow-by-default lint for unit bindings #112380

Merged
merged 1 commit into from Nov 22, 2023

Conversation

jieyouxu
Copy link
Contributor

@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. 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 #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
Contributor 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 changed the title Add allow-by-default lint for useless bindings Add allow-by-default lint for unit bindings Jun 7, 2023
compiler/rustc_lint_defs/src/lib.rs Outdated Show resolved Hide resolved
compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs Outdated Show resolved Hide resolved
compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs Outdated Show resolved Hide resolved
@WaffleLapkin WaffleLapkin self-assigned this Jun 8, 2023
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
Contributor 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.

@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
Contributor Author

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

@jieyouxu
Copy link
Contributor 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
Contributor 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
Contributor 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.

@rust-log-analyzer

This comment has been minimized.

@WaffleLapkin
Copy link
Member

@bors r+

@bors
Copy link
Contributor

bors commented Nov 19, 2023

📌 Commit f0520fa 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 19, 2023
@bors
Copy link
Contributor

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
Contributor

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.
@WaffleLapkin
Copy link
Member

@bors r+

@bors
Copy link
Contributor

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
Contributor

bors commented Nov 22, 2023

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

@bors
Copy link
Contributor

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
12 checks passed
@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
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. 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. 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, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

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