mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-01 03:03:40 +00:00
Actually check lifetimes in trivially_copy_pass_by_ref
This commit is contained in:
parent
eaa03ea911
commit
2315f76f9d
@ -7,6 +7,7 @@
|
|||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(let_else)]
|
#![feature(let_else)]
|
||||||
#![feature(lint_reasons)]
|
#![feature(lint_reasons)]
|
||||||
|
#![feature(never_type)]
|
||||||
#![feature(once_cell)]
|
#![feature(once_cell)]
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
#![feature(stmt_expr_attributes)]
|
#![feature(stmt_expr_attributes)]
|
||||||
|
@ -3,17 +3,19 @@ use std::iter;
|
|||||||
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::ty::is_copy;
|
use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
|
||||||
use clippy_utils::{is_self, is_self_ty};
|
use clippy_utils::{is_self, is_self_ty};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_ast::attr;
|
use rustc_ast::attr;
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::FnKind;
|
use rustc_hir::intravisit::FnKind;
|
||||||
use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
|
use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty;
|
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
|
use rustc_middle::ty::{self, RegionKind};
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::def_id::LocalDefId;
|
use rustc_span::def_id::LocalDefId;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
@ -141,50 +143,62 @@ impl<'tcx> PassByRefOrValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let fn_sig = cx.tcx.fn_sig(def_id);
|
let fn_sig = cx.tcx.fn_sig(def_id);
|
||||||
let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);
|
|
||||||
|
|
||||||
let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
|
let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
|
||||||
|
|
||||||
for (index, (input, &ty)) in iter::zip(decl.inputs, fn_sig.inputs()).enumerate() {
|
// Gather all the lifetimes found in the output type which may affect whether
|
||||||
|
// `TRIVIALLY_COPY_PASS_BY_REF` should be linted.
|
||||||
|
let mut output_regions = FxHashSet::default();
|
||||||
|
for_each_top_level_late_bound_region(fn_sig.skip_binder().output(), |region| -> ControlFlow<!> {
|
||||||
|
output_regions.insert(region);
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
});
|
||||||
|
|
||||||
|
for (index, (input, ty)) in iter::zip(
|
||||||
|
decl.inputs,
|
||||||
|
fn_sig.skip_binder().inputs().iter().map(|&ty| fn_sig.rebind(ty)),
|
||||||
|
)
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
// All spans generated from a proc-macro invocation are the same...
|
// All spans generated from a proc-macro invocation are the same...
|
||||||
match span {
|
match span {
|
||||||
Some(s) if s == input.span => return,
|
Some(s) if s == input.span => continue,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
match ty.kind() {
|
match *ty.skip_binder().kind() {
|
||||||
ty::Ref(input_lt, ty, Mutability::Not) => {
|
ty::Ref(lt, ty, Mutability::Not) => {
|
||||||
// Use lifetimes to determine if we're returning a reference to the
|
match lt.kind() {
|
||||||
// argument. In that case we can't switch to pass-by-value as the
|
RegionKind::ReLateBound(index, region)
|
||||||
// argument will not live long enough.
|
if index.as_u32() == 0 && output_regions.contains(®ion) =>
|
||||||
let output_lts = match *fn_sig.output().kind() {
|
{
|
||||||
ty::Ref(output_lt, _, _) => vec![output_lt],
|
continue;
|
||||||
ty::Adt(_, substs) => substs.regions().collect(),
|
},
|
||||||
_ => vec![],
|
// Early bound regions on functions are either from the containing item, are bounded by another
|
||||||
};
|
// lifetime, or are used as a bound for a type or lifetime.
|
||||||
|
RegionKind::ReEarlyBound(..) => continue,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
if_chain! {
|
let ty = cx.tcx.erase_late_bound_regions(fn_sig.rebind(ty));
|
||||||
if !output_lts.contains(input_lt);
|
if is_copy(cx, ty)
|
||||||
if is_copy(cx, *ty);
|
&& let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes())
|
||||||
if let Some(size) = cx.layout_of(*ty).ok().map(|l| l.size.bytes());
|
&& size <= self.ref_min_size
|
||||||
if size <= self.ref_min_size;
|
&& let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind
|
||||||
if let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind;
|
{
|
||||||
then {
|
let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
|
||||||
let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
|
"self".into()
|
||||||
"self".into()
|
} else {
|
||||||
} else {
|
snippet(cx, decl_ty.span, "_").into()
|
||||||
snippet(cx, decl_ty.span, "_").into()
|
};
|
||||||
};
|
span_lint_and_sugg(
|
||||||
span_lint_and_sugg(
|
cx,
|
||||||
cx,
|
TRIVIALLY_COPY_PASS_BY_REF,
|
||||||
TRIVIALLY_COPY_PASS_BY_REF,
|
input.span,
|
||||||
input.span,
|
&format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size),
|
||||||
&format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size),
|
"consider passing by value instead",
|
||||||
"consider passing by value instead",
|
value_type,
|
||||||
value_type,
|
Applicability::Unspecified,
|
||||||
Applicability::Unspecified,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -196,6 +210,7 @@ impl<'tcx> PassByRefOrValue {
|
|||||||
_ => continue,
|
_ => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let ty = cx.tcx.erase_late_bound_regions(ty);
|
||||||
|
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if is_copy(cx, ty);
|
if is_copy(cx, ty);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_ast::ast::Mutability;
|
use rustc_ast::ast::Mutability;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
@ -13,8 +14,8 @@ use rustc_lint::LateContext;
|
|||||||
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
||||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
|
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, AdtDef, Binder, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy,
|
self, AdtDef, Binder, BoundRegion, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Region, RegionKind, Ty,
|
||||||
VariantDiscr,
|
TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, UintTy, VariantDiscr,
|
||||||
};
|
};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
||||||
@ -667,3 +668,30 @@ pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_each_top_level_late_bound_region<B>(
|
||||||
|
ty: Ty<'_>,
|
||||||
|
f: impl FnMut(BoundRegion) -> ControlFlow<B>,
|
||||||
|
) -> ControlFlow<B> {
|
||||||
|
struct V<F> {
|
||||||
|
index: u32,
|
||||||
|
f: F,
|
||||||
|
}
|
||||||
|
impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<'tcx> for V<F> {
|
||||||
|
type BreakTy = B;
|
||||||
|
fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
|
if let RegionKind::ReLateBound(idx, bound) = r.kind() && idx.as_u32() == self.index {
|
||||||
|
(self.f)(bound)
|
||||||
|
} else {
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
|
||||||
|
self.index += 1;
|
||||||
|
let res = t.super_visit_with(self);
|
||||||
|
self.index -= 1;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty.visit_with(&mut V { index: 0, f })
|
||||||
|
}
|
||||||
|
@ -113,6 +113,32 @@ mod issue5876 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn _ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> {
|
||||||
|
Some(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_lifetimes)]
|
||||||
|
fn _ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> {
|
||||||
|
Some(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 {
|
||||||
|
if true { x } else { y }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn _async_implicit(x: &u32) -> &u32 {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_lifetimes)]
|
||||||
|
async fn _async_explicit<'a>(x: &'a u32) -> &'a u32 {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (mut foo, bar) = (Foo(0), Bar([0; 24]));
|
let (mut foo, bar) = (Foo(0), Bar([0; 24]));
|
||||||
let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
|
let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
|
||||||
|
@ -106,5 +106,11 @@ error: this argument (N byte) is passed by reference, but would be more efficien
|
|||||||
LL | fn foo(x: &i32) {
|
LL | fn foo(x: &i32) {
|
||||||
| ^^^^ help: consider passing by value instead: `i32`
|
| ^^^^ help: consider passing by value instead: `i32`
|
||||||
|
|
||||||
error: aborting due to 17 previous errors
|
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
|
||||||
|
--> $DIR/trivially_copy_pass_by_ref.rs:138:37
|
||||||
|
|
|
||||||
|
LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
|
||||||
|
| ^^^^^^^ help: consider passing by value instead: `u32`
|
||||||
|
|
||||||
|
error: aborting due to 18 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user