Skip to content

Commit

Permalink
Last commit
Browse files Browse the repository at this point in the history
  • Loading branch information
blyxyas committed Feb 14, 2023
1 parent edb006f commit 8e032a6
Show file tree
Hide file tree
Showing 2 changed files with 361 additions and 126 deletions.
275 changes: 149 additions & 126 deletions clippy_lints/src/functions/hidden_static_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,124 +7,149 @@ use rustc_hir::{
};
use rustc_lint::LateContext;
use rustc_middle::lint::in_external_macro;
use rustc_span::Span;
use rustc_span::{symbol::Ident, Span};

use super::HIDDEN_STATIC_LIFETIME;

// As a summary:
//
// A lifetime can only be changed if:
// * Used in immutable references.
// * Not behind a mutable reference.
// * Not used in function types
//
// NOTE: Struct's fields follow the same rules as types

pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, span: Span) {
if !in_external_macro(cx.sess(), span) &&
let FnKind::ItemFn(_, generics, _) = kind {

let mut v = FuncVisitor {
ret_ty: &decl.output,
inputs: decl.inputs,
predicates: &generics.predicates,
lifetime_is_used: false
};

for param in generics.params {
if let GenericParamKind::Lifetime { kind } = param.kind && kind != LifetimeParamKind::Elided {
v.visit_generic_param(param);
if !v.lifetime_is_used {
span_lint(cx, HIDDEN_STATIC_LIFETIME, param.span, "hi");
};
}
};
}
struct V<'a> {
// (Lifetime, Bounded typ)
lifetimes: Vec<&'a GenericParam<'a>>,
}

struct FuncVisitor<'a> {
ret_ty: &'a FnRetTy<'a>,
inputs: &'a [Ty<'a>],
predicates: &'a [WherePredicate<'a>],
lifetime_is_used: bool,
}
impl<'a> Visitor<'_> for V<'a> {
/// Remove all mutable lifetimes that aren't for T: 'static
fn visit_where_predicate(&mut self, predicate: &WherePredicate<'_>) {
if let WherePredicate::BoundPredicate(pred) = predicate {
for bound in pred.bounds {
// Check (for each lifetime) that the type they're associated with is 'static.
continue;
}
}
}

impl<'v> Visitor<'_> for FuncVisitor<'v> {
fn visit_generic_param(&mut self, param: &GenericParam<'_>) {
// Check inputs
for input in self.inputs {
if ref_uses_lifetime(input, param) || check_path(input, param) {
dbg!("@@@@@@@@@@@@@@@@@@@@@@@@");
self.lifetime_is_used = true;
return;
};
}
fn visit_ty(&mut self, ty: &Ty<'_>) {
let mut outer_continue: bool;
let mut outer_break = false;
let mut i = 0;
while i < self.lifetimes.len() {
outer_continue = false;
let lifetime = self.lifetimes[i];

// Check references

let mut final_ty = ty;
let mut behind_mut_ref = false;
while let TyKind::Ref(lt, mut_ty) = &final_ty.kind {
if mut_ty.mutbl.is_mut() {
behind_mut_ref = true;
};

// Check return
if let FnRetTy::Return(ret_ty) = self.ret_ty {
if ref_uses_lifetime(ret_ty, param) || check_path(ret_ty, param) {
dbg!("============================");
self.lifetime_is_used = true;
return;
};
};
if lt.ident.name == lifetime.name.ident().name || behind_mut_ref {
if self.lifetimes.is_empty() {
outer_continue = true;
continue;
}

self.lifetimes.remove(i);
outer_continue = true;

}
final_ty = mut_ty.ty;
}

// Check predicates
if outer_continue {
continue;
} else if outer_break {
break;
}

for predicate in self.predicates {
for bound in predicate.bounds() {
if let GenericBound::Outlives(lifetime) = bound &&
lifetime.ident.name == param.name.ident().name {
self.lifetime_is_used = true;
return;
// Now final_ty is equal to ty.peel_refs()
// Check Paths:

if let TyKind::Path(QPath::Resolved(_, path)) = final_ty.kind {
for segment in path.segments {
for argument in segment.args().args {
if let GenericArg::Lifetime(lt) = argument && lt.ident.name == lifetime.name.ident().name {
self.lifetimes.remove(i);
outer_continue = true;
continue;
};
}
if outer_continue {
break;
}
}
};

if outer_continue {
continue;
}
i += 1;
}
}
}

fn ref_uses_lifetime(mut ty: &Ty<'_>, lifetime: &GenericParam<'_>) -> bool {
while let TyKind::Ref(lt_ref, mut_ty) = &ty.kind {
if lt_ref.ident.name == lifetime.name.ident().name && mut_ty.mutbl.is_not() {
return true;
} else {
ty = mut_ty.ty;
fn visit_fn_ret_ty<'v>(&mut self, ret_ty: &'v FnRetTy<'v>) {
if let FnRetTy::Return(ty) = ret_ty {
let mut i = 0;
while i < self.lifetimes.len() {
dbg!(self.lifetimes[i].name.ident().as_str());
if ty_uses_lifetime(ty, &self.lifetimes[i], &self) {
self.lifetimes.remove(i);
}
i += 1;
}
}
}
false
}

fn check_path(ty: &Ty<'_>, lifetime: &GenericParam<'_>) -> bool {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.peel_refs().kind {
for segment in path.segments {
for arg in segment.args().args {
if let GenericArg::Lifetime(lt_arg) = arg {
if lt_arg.ident.name == lifetime.name.ident().name {
return true;
};
} else if let &GenericArg::Type(ty) = arg {
dbg!("!!!!!!!!!!!!!!!!!!!!!!!!!");
return check_all_types(ty, lifetime);
};
}
}
};
false
pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, span: Span) {
if !in_external_macro(cx.sess(), span) &&
let FnKind::ItemFn(_, generics, _) = kind {
let mut visitor = V {
lifetimes: Vec::new()
};

// Fill visitor.lifetimes with function's lifetimes

for generic in generics.params {
if let GenericParamKind::Lifetime { .. } = generic.kind {
visitor.lifetimes.push(generic);
};
};

for input in decl.inputs {
visitor.visit_ty(input);
}

for predicate in generics.predicates {
visitor.visit_where_predicate(predicate);
}
visitor.visit_fn_ret_ty(&decl.output);

for generic in visitor.lifetimes {
span_lint_and_help(cx,
HIDDEN_STATIC_LIFETIME,
generic.span,
"this lifetime can be changed to `'static`",
None,
&format!("try removing the lifetime parameter `{}` and changing references to `'static`", generic.name.ident().as_str()));
}
};
}

fn check_all_types(ty: &Ty<'_>, lifetime: &GenericParam<'_>) -> bool {
fn ty_uses_lifetime(ty: &Ty<'_>, generic: &GenericParam<'_>) -> bool {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
fn ty_uses_lifetime(ty: &Ty<'_>, generic: &GenericParam, v: &V<'_>) -> bool {
fn check_ty(ty: &Ty<'_>, generic: &GenericParam, v: &V<'_>) -> bool {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
for segment in path.segments {
if let Some(GenericArgs { args, .. }) = segment.args {
for arg in args.iter() {
if let GenericArg::Lifetime(lifetime) = arg {
if lifetime.ident.name == generic.name.ident().name {
if lifetime.ident.name == generic.name.ident().name {
// generic is used
return true;
}
} else if let GenericArg::Type(ty) = arg {
return classify_ty(ty, generic);
return classify_ty(ty, generic, v);
}
}
}
Expand All @@ -134,37 +159,34 @@ fn check_all_types(ty: &Ty<'_>, lifetime: &GenericParam<'_>) -> bool {
}

#[inline]
fn barefn_uses_lifetime(barefn: &BareFnTy<'_>, generic: &GenericParam<'_>) -> bool {
// Check inputs
for input in barefn.decl.inputs {
if ref_uses_lifetime(input, generic) || check_path(input, generic) {
return false;
};
}

// Check return
if let FnRetTy::Return(ret_ty) = barefn.decl.output {
if check_path(ret_ty, generic) {
return false;
};
};
true
}
fn barefn_uses_lifetime(barefn: &BareFnTy<'_>, generic: &GenericParam, v: &V<'_>) -> bool {
// let mut visitor = V {
// lifetimes: v.lifetimes.clone(),
// };

// for input in barefn.decl.inputs {
// visitor.visit_ty(input);
// }

// visitor.visit_fn_ret_ty(&barefn.decl.output);
// !visitor.lifetimes.contains(&generic)
true
}

#[inline]
fn tuple_uses_lifetime(tuple: &[Ty<'_>], generic: &GenericParam<'_>) -> bool {
tuple.iter().any(|ty| classify_ty(ty, generic))
fn tuple_uses_lifetime(tuple: &[Ty<'_>], generic: &GenericParam, v: &V<'_>) -> bool {
tuple.iter().any(|ty| classify_ty(ty, generic, v))
}

fn opaquedef_uses_lifetime(args: &[GenericArg<'_>], generic: &GenericParam<'_>) -> bool {
fn opaquedef_uses_lifetime(args: &[GenericArg<'_>], generic: &GenericParam, v: &V<'_>) -> bool {
for arg in args.iter() {
if let GenericArg::Lifetime(lifetime) = arg {
if lifetime.ident.name == generic.name.ident().name {
// generic is used
return true;
}
} else if let GenericArg::Type(ty) = arg {
return classify_ty(ty, generic);
return classify_ty(ty, generic, v);
}
}
false
Expand All @@ -173,40 +195,41 @@ fn check_all_types(ty: &Ty<'_>, lifetime: &GenericParam<'_>) -> bool {
#[inline]
fn traitobject_uses_lifetime(lifetime: &Lifetime, traits: &[PolyTraitRef<'_>], generic: &GenericParam<'_>) -> bool {
if lifetime.ident.name == generic.name.ident().name {
return true;
return false;
}
for PolyTraitRef {
bound_generic_params, ..
} in traits
{
if bound_generic_params.iter().any(|param| param.def_id == generic.def_id) {
if bound_generic_params
.iter()
.any(|param| param.name.ident().name == generic.name.ident().name)
{
return true;
};
}
false
}

#[inline]
fn classify_ty(ty: &Ty<'_>, generic: &GenericParam<'_>) -> bool {
fn classify_ty(ty: &Ty<'_>, generic: &GenericParam, v: &V<'_>) -> bool {
match &ty.kind {
TyKind::Slice(ty) | TyKind::Array(ty, _) => ty_uses_lifetime(ty, generic),
TyKind::Ptr(mut_ty) => ty_uses_lifetime(mut_ty.ty, generic),
TyKind::BareFn(barefnty) => barefn_uses_lifetime(barefnty, generic),
TyKind::Tup(tuple) => tuple_uses_lifetime(tuple, generic),
TyKind::Path(_) => ty_uses_lifetime(ty, generic),
TyKind::OpaqueDef(_, genericargs, _) => opaquedef_uses_lifetime(genericargs, generic),
TyKind::Slice(ty) | TyKind::Array(ty, _) => check_ty(ty, generic, v),
TyKind::Ptr(mut_ty) => check_ty(mut_ty.ty, generic, v),
TyKind::BareFn(barefnty) => {
barefn_uses_lifetime(barefnty, generic, v)},
TyKind::Tup(tuple) => tuple_uses_lifetime(tuple, generic, v),
TyKind::Path(_) => check_ty(ty, generic, v),
TyKind::OpaqueDef(_, genericargs, _) => opaquedef_uses_lifetime(genericargs, generic, v),
TyKind::TraitObject(poly_trait_ref, lifetime, _) =>
traitobject_uses_lifetime(lifetime, poly_trait_ref, generic),
TyKind::Typeof(_) // This is unused for now, this needs revising when Typeof is used.
| TyKind::Err
| TyKind::Never => false,
TyKind::Ref(_, MutTy { ty, ..}) => ref_uses_lifetime(ty, generic),
TyKind::Infer => true,
| TyKind::Never
| TyKind::Ref(_, _)
| TyKind::Infer => true,
}
}

// Separate refs from ty.

// Now final_ty is equivalent to ty.peel_refs
return classify_ty(ty.peel_refs(), lifetime);
return classify_ty(ty.peel_refs(), &generic, v);
}
Loading

0 comments on commit 8e032a6

Please sign in to comment.