Skip to content

Pass opaque types for the type_alias_bounds lint #108663

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

Closed
wants to merge 8 commits into from
Closed
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
68 changes: 64 additions & 4 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1461,19 +1461,79 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
let hir::ItemKind::TyAlias(ty, type_alias_generics) = &item.kind else {
return
};
if let hir::TyKind::OpaqueDef(..) = ty.kind {
// Bounds are respected for `type X = impl Trait`
return;
}

// There must not be a where clause
if type_alias_generics.predicates.is_empty() {
return;
}

// Collects generic parameters used in non-opaque types
use rustc_hir::intravisit::{self, Visitor};
struct NonOpaqueVisitor {
has_opaque: bool,
ty_params: FxHashSet<DefId>,
lifetimes: FxHashSet<Symbol>,
}
impl<'tcx> Visitor<'tcx> for NonOpaqueVisitor {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
// if ty is not an opaque type, we need to check its children
if !matches!(ty.kind, hir::TyKind::OpaqueDef(..)) {
intravisit::walk_ty(self, ty);
} else {
self.has_opaque = true;
}

if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = ty.kind {
if let Res::Def(DefKind::TyParam, def_id) = path.res {
// collect ty parameters
self.ty_params.insert(def_id.clone());
}
}
}

fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
// collect lifetime parameters
self.lifetimes.insert(lifetime.ident.name.clone());
}
}

let mut non_opaque_visitor = NonOpaqueVisitor {
has_opaque: false,
ty_params: FxHashSet::default(),
lifetimes: FxHashSet::default(),
};
non_opaque_visitor.visit_ty(ty);

let NonOpaqueVisitor { has_opaque, ty_params, lifetimes } = non_opaque_visitor;

let mut where_spans = Vec::new();
let mut inline_spans = Vec::new();
let mut inline_sugg = Vec::new();
for p in type_alias_generics.predicates {
// Warn bounds if there is no opaque type
if has_opaque {
// Warn bounds whose ty is a generic-param used directly in non-opaque types when there is an opaque type
let mut is_generic_param_used_in_non_opaque_ty = false;
if let hir::WherePredicate::BoundPredicate(ref bound_pred) = p {
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
bound_pred.bounded_ty.kind
{
if let Res::Def(DefKind::TyParam, def_id) = path.res {
if ty_params.contains(&def_id) {
is_generic_param_used_in_non_opaque_ty = true;
}
}
}
} else if let hir::WherePredicate::RegionPredicate(ref region_pred) = p {
if lifetimes.contains(&region_pred.lifetime.ident.name) {
is_generic_param_used_in_non_opaque_ty = true;
}
}
if !is_generic_param_used_in_non_opaque_ty {
continue;
}
}

let span = p.span();
if p.in_where_clause() {
where_spans.push(span);
Expand Down
37 changes: 37 additions & 0 deletions tests/ui/type-alias-impl-trait/type-alias-bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Test `type_alias_bounds` lint warning about bounds in type-alias-impl-trait.

// check-pass
#![feature(type_alias_impl_trait)]
#![allow(dead_code)]

use std::fmt::Debug;

type T1<T: Debug> = (impl Debug, T); //~ WARN not enforced in type aliases
fn f1<U: Debug>(x: U) -> T1<U> {
(Vec::<U>::new(), x)
}

type T2<T: Debug> = (impl Debug, usize); // no warning here!
fn f2<U: Debug>() -> T2<U> {
(Vec::<U>::new(), 0)
}

trait Bar<T: Debug> {}
impl<T: Debug> Bar<T> for Vec<T> {}

type T3<T: Debug> = (impl Bar<T>, T); //~ WARN not enforced in type aliases
fn f3<U: Debug>(v: U) -> T3<U> {
(Vec::<U>::new(), v)
}

type T4<T: Debug> = (impl Bar<T>, usize); // no warning here!
fn f4<U: Debug>() -> T4<U> {
(Vec::<U>::new(), 0)
}

type T5<'a: 'a, T: Debug> = (impl Debug, &'a usize); //~ WARN not enforced in type aliases
fn f5<'a, T: Debug>(x: &'a usize) -> T5<'a, T> {
(Vec::<T>::new(), x)
}

fn main() {}
39 changes: 39 additions & 0 deletions tests/ui/type-alias-impl-trait/type-alias-bounds.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:9:12
|
LL | type T1<T: Debug> = (impl Debug, T);
| ^^^^^
|
= note: `#[warn(type_alias_bounds)]` on by default
help: the bound will not be checked when the type alias is used, and should be removed
|
LL - type T1<T: Debug> = (impl Debug, T);
LL + type T1<T> = (impl Debug, T);
|

warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:22:12
|
LL | type T3<T: Debug> = (impl Bar<T>, T);
| ^^^^^
|
help: the bound will not be checked when the type alias is used, and should be removed
|
LL - type T3<T: Debug> = (impl Bar<T>, T);
LL + type T3<T> = (impl Bar<T>, T);
|

warning: bounds on generic parameters are not enforced in type aliases
--> $DIR/type-alias-bounds.rs:32:13
|
LL | type T5<'a: 'a, T: Debug> = (impl Debug, &'a usize);
| ^^
|
help: the bound will not be checked when the type alias is used, and should be removed
|
LL - type T5<'a: 'a, T: Debug> = (impl Debug, &'a usize);
LL + type T5<'a, T: Debug> = (impl Debug, &'a usize);
|

warning: 3 warnings emitted

2 changes: 1 addition & 1 deletion tests/ui/type/type-alias-bounds.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Test `ignored_generic_bounds` lint warning about bounds in type aliases.
// Test `type_alias_bounds` lint warning about bounds in type aliases.

// check-pass
#![allow(dead_code)]
Expand Down