-
Notifications
You must be signed in to change notification settings - Fork 14k
Unconstrained parameter fix #148788
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
base: main
Are you sure you want to change the base?
Unconstrained parameter fix #148788
Changes from all commits
c18b48e
73b49d7
fb612ef
1230bc7
3bf7583
c8905fa
3b05974
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -9,14 +9,18 @@ | |||||
| //! fixed, but for the moment it's easier to do these checks early. | ||||||
|
|
||||||
| use std::assert_matches::debug_assert_matches; | ||||||
| use std::ops::ControlFlow; | ||||||
|
|
||||||
| use min_specialization::check_min_specialization; | ||||||
| use rustc_data_structures::fx::FxHashSet; | ||||||
| use rustc_errors::Applicability; | ||||||
| use rustc_errors::codes::*; | ||||||
| use rustc_errors::{Applicability, Diag}; | ||||||
| use rustc_hir::def::DefKind; | ||||||
| use rustc_hir::def_id::LocalDefId; | ||||||
| use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; | ||||||
| use rustc_hir::def_id::{DefId, LocalDefId}; | ||||||
| use rustc_hir::intravisit::{self, Visitor, walk_lifetime}; | ||||||
| use rustc_hir::{HirId, LifetimeKind, Path, QPath, Ty, TyKind}; | ||||||
| use rustc_middle::hir::nested_filter::All; | ||||||
| use rustc_middle::ty::{self, GenericParamDef, TyCtxt, TypeVisitableExt}; | ||||||
| use rustc_span::{ErrorGuaranteed, kw}; | ||||||
|
|
||||||
| use crate::constrained_generic_params as cgp; | ||||||
|
|
@@ -172,6 +176,13 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( | |||||
| ); | ||||||
| } | ||||||
| } | ||||||
| suggest_to_remove_or_use_generic( | ||||||
| tcx, | ||||||
| &mut diag, | ||||||
| impl_def_id, | ||||||
| param, | ||||||
| "lifetime", | ||||||
| ); | ||||||
| res = Err(diag.emit()); | ||||||
| } | ||||||
| } | ||||||
|
|
@@ -235,8 +246,130 @@ pub(crate) fn enforce_impl_non_lifetime_params_are_constrained( | |||||
| const_param_note2: const_param_note, | ||||||
| }); | ||||||
| diag.code(E0207); | ||||||
| suggest_to_remove_or_use_generic(tcx, &mut diag, impl_def_id, ¶m, "type"); | ||||||
| res = Err(diag.emit()); | ||||||
| } | ||||||
| } | ||||||
| res | ||||||
| } | ||||||
|
|
||||||
| /// A HIR visitor that checks if a specific generic parameter (by its `DefId`) | ||||||
| /// is used within a given HIR tree. | ||||||
| struct ParamUsageVisitor<'tcx> { | ||||||
| tcx: TyCtxt<'tcx>, | ||||||
| /// The `DefId` of the generic parameter we are looking for. | ||||||
| param_def_id: DefId, | ||||||
| found: bool, | ||||||
| } | ||||||
|
|
||||||
| // todo: maybe this can be done more efficiently by only searching for generics OR lifetimes and searching more effectively | ||||||
| impl<'tcx> Visitor<'tcx> for ParamUsageVisitor<'tcx> { | ||||||
| type NestedFilter = All; | ||||||
|
|
||||||
| fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { | ||||||
| self.tcx | ||||||
| } | ||||||
|
|
||||||
| /// We use `ControlFlow` to stop visiting as soon as we find what we're looking for. | ||||||
| type Result = ControlFlow<()>; | ||||||
|
|
||||||
| /// This is the primary method for finding usages of type or const parameters. | ||||||
| fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) -> Self::Result { | ||||||
| if let Some(res_def_id) = path.res.opt_def_id() { | ||||||
| if res_def_id == self.param_def_id { | ||||||
| self.found = true; | ||||||
| return ControlFlow::Break(()); // Found it, stop visiting. | ||||||
| } | ||||||
| } | ||||||
| // If not found, continue walking down the HIR tree. | ||||||
| intravisit::walk_path(self, path) | ||||||
| } | ||||||
|
|
||||||
| fn visit_lifetime(&mut self, lifetime: &'tcx rustc_hir::Lifetime) -> Self::Result { | ||||||
| if let LifetimeKind::Param(id) = lifetime.kind { | ||||||
| if let Some(local_def_id) = self.param_def_id.as_local() { | ||||||
| if id == local_def_id { | ||||||
| self.found = true; | ||||||
| return ControlFlow::Break(()); // Found it, stop visiting. | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| walk_lifetime(self, lifetime) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| fn suggest_to_remove_or_use_generic( | ||||||
| tcx: TyCtxt<'_>, | ||||||
| diag: &mut Diag<'_>, | ||||||
| impl_def_id: LocalDefId, | ||||||
| param: &GenericParamDef, | ||||||
| parameter_type: &str, | ||||||
| ) { | ||||||
| let node = tcx.hir_node_by_def_id(impl_def_id); | ||||||
| let hir_impl = node.expect_item().expect_impl(); | ||||||
|
|
||||||
| let Some((index, _)) = hir_impl | ||||||
| .generics | ||||||
| .params | ||||||
| .iter() | ||||||
| .enumerate() | ||||||
| .find(|(_, par)| par.def_id.to_def_id() == param.def_id) | ||||||
| else { | ||||||
| return; | ||||||
| }; | ||||||
|
|
||||||
| // search if the parameter is used in the impl body | ||||||
| let mut visitor = ParamUsageVisitor { | ||||||
| tcx, // Pass the TyCtxt | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| param_def_id: param.def_id, | ||||||
| found: false, | ||||||
| }; | ||||||
|
|
||||||
| for item_ref in hir_impl.items { | ||||||
| let _ = visitor.visit_impl_item_ref(item_ref); | ||||||
| if visitor.found { | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| let is_param_used = visitor.found; | ||||||
|
|
||||||
| // Suggestion for removing the type parameter. | ||||||
| let mut suggestions = vec![]; | ||||||
| if !is_param_used { | ||||||
| // Only suggest removing it if it's not used anywhere. | ||||||
| suggestions.push(vec![(hir_impl.generics.span_for_param_removal(index), String::new())]); | ||||||
| } | ||||||
|
|
||||||
| // Suggestion for making use of the type parameter. | ||||||
| if let Some(path) = extract_ty_as_path(hir_impl.self_ty) { | ||||||
| let seg = path.segments.last().unwrap(); | ||||||
| if let Some(args) = seg.args { | ||||||
| suggestions | ||||||
| .push(vec![(args.span().unwrap().shrink_to_hi(), format!(", {}", param.name))]); | ||||||
| } else { | ||||||
| suggestions.push(vec![(seg.ident.span.shrink_to_hi(), format!("<{}>", param.name))]); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| let msg = if is_param_used { | ||||||
| // If it's used, the only valid fix is to constrain it. | ||||||
| format!("make use of the {} parameter `{}` in the `self` type", parameter_type, param.name) | ||||||
| } else { | ||||||
| format!( | ||||||
| "either remove the unused {} parameter `{}`, or make use of it", | ||||||
| parameter_type, param.name | ||||||
| ) | ||||||
| }; | ||||||
|
|
||||||
| diag.multipart_suggestions(msg, suggestions, Applicability::MaybeIncorrect); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we should use a multi-part suggestion for this, these should be multiple distinct suggestions |
||||||
| } | ||||||
|
|
||||||
| fn extract_ty_as_path<'hir>(ty: &Ty<'hir>) -> Option<&'hir Path<'hir>> { | ||||||
| match ty.kind { | ||||||
| TyKind::Path(QPath::Resolved(_, path)) => Some(path), | ||||||
| TyKind::Slice(ty) | TyKind::Array(ty, _) => extract_ty_as_path(ty), | ||||||
| TyKind::Ptr(ty) | TyKind::Ref(_, ty) => extract_ty_as_path(ty.ty), | ||||||
| _ => None, | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,14 @@ error[E0207]: the type parameter `Q` is not constrained by the impl trait, self | |
| | | ||
| LL | unsafe impl<Q: Trait> Send for Inner {} | ||
| | ^ unconstrained type parameter | ||
| | | ||
| help: either remove the unused type parameter `Q`, or make use of it | ||
| | | ||
| LL - unsafe impl<Q: Trait> Send for Inner {} | ||
| LL + unsafe impl Send for Inner {} | ||
| | | ||
| LL | unsafe impl<Q: Trait> Send for Inner<Q> {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should only suggest this if it could be correct (maybe by checking if the self type has generic parameters), or we should be very clear that it's just illustrative:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well there would another error if the struct Foo<N> {x: N}
impl<N> Foo {}which obviously gives the error, that the generic parameter N should be provided for the implementation of Foo: error[E0107]: missing generics for struct `Foo`
--> src/lib.rs:3:9
|
3 | impl<N> Foo {}
| ^^^ expected 1 generic argument
|
note: struct defined here, with 1 generic parameter: `N`
--> src/lib.rs:1:8
|
1 | struct Foo<N> {x: N}
| ^^^ -
help: add missing generic argument
|
3 | impl<N> Foo<N> {}
| +++
For more information about this error, try `rustc --explain E0107`.Therefore I would even suggest never suggesting to add the generic argument in general or did I miss some edgecase? |
||
| | +++ | ||
|
|
||
| error: aborting due to 1 previous error | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,11 @@ error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, | |
| | | ||
| LL | impl<'a> Foo<fn(&())> { | ||
| | ^^ unconstrained lifetime parameter | ||
| | | ||
| help: make use of the lifetime parameter `'a` in the `self` type | ||
| | | ||
| LL | impl<'a> Foo<fn(&()), 'a> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly to above, we should make sure this is correct or clearly indicate that it is purely illustrative. |
||
| | ++++ | ||
|
|
||
| error[E0308]: mismatched types | ||
| --> $DIR/hr-do-not-blame-outlives-static-ice.rs:14:11 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the comments in this impl feel redundant