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

Rudimentary heuristic to insert parentheses when needed for RPIT overcaptures lint #134142

Merged
merged 1 commit into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions compiler/rustc_trait_selection/src/errors.rs
Copy link
Member

@fmease fmease Dec 10, 2024

Choose a reason for hiding this comment

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

We don't have basically any preexisting machinery to detect when parentheses are needed for types

We have hir::Generics::bounds_span_for_suggestions() for bounds (on generic params I guess?). I don't know if you can leverage it here in some way or generalize it. I haven't looked at it for a while.

Edit: And yeah, it checks for TyKind::Ref but not for TyKind::Ptr, I've been meaning to fix that for some time.

cc #120929

Copy link
Member Author

Choose a reason for hiding this comment

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

That's more about ambiguities introduced by the bound of the RPIT themselves. This fixes ambiguities having to do with the parent type of the RPIT. I think both could use fixing and unification, but I don't think I'll do it here.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{Visitor, walk_ty};
use rustc_hir::{FnRetTy, GenericParamKind};
use rustc_hir::{FnRetTy, GenericParamKind, Node};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt};
Expand Down Expand Up @@ -1888,10 +1888,35 @@ pub fn impl_trait_overcapture_suggestion<'tcx>(
.collect::<Vec<_>>()
.join(", ");

suggs.push((
tcx.def_span(opaque_def_id).shrink_to_hi(),
format!(" + use<{concatenated_bounds}>"),
));
let opaque_hir_id = tcx.local_def_id_to_hir_id(opaque_def_id);
// FIXME: This is a bit too conservative, since it ignores parens already written in AST.
let (lparen, rparen) = match tcx
.hir()
.parent_iter(opaque_hir_id)
.nth(1)
.expect("expected ty to have a parent always")
.1
{
Node::PathSegment(segment)
if segment.args().paren_sugar_output().is_some_and(|ty| ty.hir_id == opaque_hir_id) =>
{
("(", ")")
}
Node::Ty(ty) => match ty.kind {
rustc_hir::TyKind::Ptr(_) | rustc_hir::TyKind::Ref(..) => ("(", ")"),
// FIXME: RPITs are not allowed to be nested in `impl Fn() -> ...`,
// but we eventually could support that, and that would necessitate
// making this more sophisticated.
_ => ("", ""),
},
_ => ("", ""),
};

let rpit_span = tcx.def_span(opaque_def_id);
if !lparen.is_empty() {
suggs.push((rpit_span.shrink_to_lo(), lparen.to_string()));
}
suggs.push((rpit_span.shrink_to_hi(), format!(" + use<{concatenated_bounds}>{rparen}")));

Some(AddPreciseCapturingForOvercapture { suggs, apit_spans })
}
Expand Down
4 changes: 4 additions & 0 deletions tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ async fn async_fn<'a>(x: &'a ()) -> impl Sized + use<> {}
//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
//~| WARN this changes meaning in Rust 2024

pub fn parens(x: &i32) -> &(impl Clone + use<>) { x }
//~^ ERROR `impl Clone` will capture more lifetimes than possibly intended in edition 2024
//~| WARN this changes meaning in Rust 2024

fn main() {}
4 changes: 4 additions & 0 deletions tests/ui/impl-trait/precise-capturing/overcaptures-2024.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ async fn async_fn<'a>(x: &'a ()) -> impl Sized {}
//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
//~| WARN this changes meaning in Rust 2024

pub fn parens(x: &i32) -> &impl Clone { x }
//~^ ERROR `impl Clone` will capture more lifetimes than possibly intended in edition 2024
//~| WARN this changes meaning in Rust 2024

fn main() {}
21 changes: 20 additions & 1 deletion tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,24 @@ help: use the precise capturing `use<...>` syntax to make the captures explicit
LL | async fn async_fn<'a>(x: &'a ()) -> impl Sized + use<> {}
| +++++++

error: aborting due to 7 previous errors
error: `impl Clone` will capture more lifetimes than possibly intended in edition 2024
--> $DIR/overcaptures-2024.rs:45:28
|
LL | pub fn parens(x: &i32) -> &impl Clone { x }
| ^^^^^^^^^^
|
= warning: this changes meaning in Rust 2024
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rpit-lifetime-capture.html>
note: specifically, this lifetime is in scope but not mentioned in the type's bounds
--> $DIR/overcaptures-2024.rs:45:18
|
LL | pub fn parens(x: &i32) -> &impl Clone { x }
| ^
= note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024
help: use the precise capturing `use<...>` syntax to make the captures explicit
|
LL | pub fn parens(x: &i32) -> &(impl Clone + use<>) { x }
| + ++++++++

error: aborting due to 8 previous errors

Loading