Repository navigation
false positive for if-let-rescope on enums when the pattern exhaustively matches all cases with significant-drop fields #137376
Copy link
Copy link
Open
Labels
A-diagnosticsArea: Messages for errors, warnings, and lintsArea: Messages for errors, warnings, and lintsA-edition-2024Area: The 2024 editionArea: The 2024 editionA-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.Area: Lints (warnings about flaws in source code) such as unused_mut.C-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.I-edition-triagedIssue: This issue has been reviewed and triaged by the Edition team.Issue: This issue has been reviewed and triaged by the Edition team.L-false-positiveLint: False positive (should not have fired).Lint: False positive (should not have fired).L-if_let_rescopeLint: if_let_rescopeLint: if_let_rescopeT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
Description
Coming from #133167 (comment) and from this new forum thread, my takeaway of some remaining clear false positives is:
-
if the
if letmatches on an enum and only some enum variant(s) have fields with potentially-significant destructor, but those are exhaustively matched in the non-elsecase of theif let, then there can be no actual change in behavior, because theelsecase is only reached in case the drop is not significant -
in the case of
cargo-edit, this wasif let Some(…irrefutable…) = expron anOption<T> -
in the urlo thread, I’ve identified the case of
if let Err(…irrefutable…)on aResult<(), E>
Here’s a simple repro for an Option case:
Code
#![allow(unused)]
#![warn(if_let_rescope)]
struct Struct;
impl Drop for Struct {
fn drop(&mut self) {}
}
fn f(option_s: Option<Struct>) {
if let Some(s) = option_s {
// …
} else {
// …
}
}Here’s the code in the playground.
Current output
warning: `if let` assigns a shorter lifetime since Edition 2024
--> src/lib.rs:10:8
|
10 | if let Some(s) = option_s {
| ^^^^^^^^^^^^^^--------
| |
| this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
|
= warning: this changes meaning in Rust 2024
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
help: the value is now dropped here in Edition 2024
--> src/lib.rs:12:5
|
12 | } else {
| ^
note: the lint level is defined here
--> src/lib.rs:2:9
|
2 | #![warn(if_let_rescope)]
| ^^^^^^^^^^^^^^
help: a `match` with a single arm can preserve the drop order up to Edition 2021
|
10 ~ match option_s { Some(s) => {
11 | // …
12 ~ } _ => {
13 | // …
14 ~ }}
|
@rustbot label A-edition-2024, A-lints, L-if_let_rescope, C-enhancement
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
A-diagnosticsArea: Messages for errors, warnings, and lintsArea: Messages for errors, warnings, and lintsA-edition-2024Area: The 2024 editionArea: The 2024 editionA-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.Area: Lints (warnings about flaws in source code) such as unused_mut.C-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.I-edition-triagedIssue: This issue has been reviewed and triaged by the Edition team.Issue: This issue has been reviewed and triaged by the Edition team.L-false-positiveLint: False positive (should not have fired).Lint: False positive (should not have fired).L-if_let_rescopeLint: if_let_rescopeLint: if_let_rescopeT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
Activity
traviscross commentedon Feb 22, 2025
cc @dingxiangfei2009 @nikomatsakis
if-let-rescopeonhashbrown::HashMap::get? #137411 as a duplicate of this issueif-let-rescopeonhashbrown::HashMap::get? #137411 as a duplicate of this issuecompiler-errors commentedon Feb 22, 2025
steffahn commentedon Feb 23, 2025
Looking back through the actual
cargo-editcases, I might have misinterpreted those somewhat, actually.It looks like none of these 4 warnings really apply to this issue desciption
Instead, these feature 2 different things:
the last one with
if let Some((alpha, numeric)) = version.pre.as_str().split_once('.')is a case whereversionhas 2 fields, one of them the.prefield, that contain types using somestruct (i.e. simply a custom
Dropimpl for memory management)… but here actually thisversionandversion.preas well are not actually in a temporary, but simply being borrowed. This is purely #137411 territory!the other 3 are also not matching the case this issue was trying to identify
here,
Some((pre_ext, pre_ext_ver)) = prerelease_id_version(self)?is working withthis
prerelease_id_versionfunction returningCargoResult<Option<(String, Option<u64>)>>but this means that the
Optionbeing matched is actually just anOption<(String, Option<u64>)>, which are all known insignificant-drop types!instead, we look at
CargoResult<T>which is atypesynonym foranyhow::Result<T>– the problem is thus anErrvalue with custom destructor,anyhow::Errorin particularthe interesting question here instead if whether perhaps somehow one can manage to get
?-operator usage like that better "supported"…I have no idea if that’s a common case though… (false positives involving
?, that is).Because this example, too, is still a true false positive (the custom drop impl is never called at a different time between editions). At least I’m pretty sure it isn’t. The specific circumstances that might be easy to catch, and common, might be:
if let … else …it itself a statement-expression, or final block value (analogous to the considerations in Improve behavior ofIF_LET_RESCOPEaround temporaries and place expressions #137444 forif let …withoutelse), so the only effect to pay attention to in the first place would be the question of "does this code change behavior if theelseclause is reached?"Result<T, E>type in question is produced by-value from a function call, and immediately propagated with the?operator. This case seems both common and reasonably easy to detect. In this case one could disregard the temporary for the wholeResult<T, E>and only consider the drop effects of a temporary forTinstead. TheEisn’t relevant because the early-returns means that when aEwas produced, theelseis never entered!compiler-errors commentedon Feb 23, 2025
For the record, the false positives that come from
?should be fixed by #137444, at least it's fixed for: