mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-15 09:27:25 +00:00
use trait solver instead; created spaghetti code
This commit is contained in:
parent
e24fc9af23
commit
0e233492d3
3 changed files with 50 additions and 35 deletions
|
@ -21,6 +21,7 @@ extern crate rustc_ast;
|
||||||
extern crate rustc_ast_pretty;
|
extern crate rustc_ast_pretty;
|
||||||
extern crate rustc_attr;
|
extern crate rustc_attr;
|
||||||
extern crate rustc_data_structures;
|
extern crate rustc_data_structures;
|
||||||
|
extern crate rustc_const_eval;
|
||||||
// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate.
|
// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate.
|
||||||
#[allow(unused_extern_crates)]
|
#[allow(unused_extern_crates)]
|
||||||
extern crate rustc_driver;
|
extern crate rustc_driver;
|
||||||
|
|
|
@ -4,17 +4,24 @@
|
||||||
// differ from the time of `rustc` even if the name stays the same.
|
// differ from the time of `rustc` even if the name stays the same.
|
||||||
|
|
||||||
use crate::msrvs::Msrv;
|
use crate::msrvs::Msrv;
|
||||||
|
use hir::{Constness, LangItem};
|
||||||
|
use rustc_const_eval::transform::check_consts::ConstCx;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
use rustc_infer::traits::Obligation;
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind,
|
Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind,
|
||||||
Terminator, TerminatorKind,
|
Terminator, TerminatorKind,
|
||||||
};
|
};
|
||||||
|
use rustc_middle::traits::ObligationCause;
|
||||||
use rustc_middle::ty::subst::GenericArgKind;
|
use rustc_middle::ty::subst::GenericArgKind;
|
||||||
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
|
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
|
||||||
|
use rustc_middle::ty::{BoundConstness, TraitRef};
|
||||||
use rustc_semver::RustcVersion;
|
use rustc_semver::RustcVersion;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
use rustc_trait_selection::traits::SelectionContext;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
||||||
|
@ -52,14 +59,13 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
|
||||||
}
|
}
|
||||||
|
|
||||||
for local in &body.local_decls {
|
for local in &body.local_decls {
|
||||||
check_ty(tcx, local.ty, local.source_info.span, false)?;
|
check_ty(tcx, local.ty, local.source_info.span)?;
|
||||||
}
|
}
|
||||||
// impl trait is gone in MIR, so check the return type manually
|
// impl trait is gone in MIR, so check the return type manually
|
||||||
check_ty(
|
check_ty(
|
||||||
tcx,
|
tcx,
|
||||||
tcx.fn_sig(def_id).subst_identity().output().skip_binder(),
|
tcx.fn_sig(def_id).subst_identity().output().skip_binder(),
|
||||||
body.local_decls.iter().next().unwrap().source_info.span,
|
body.local_decls.iter().next().unwrap().source_info.span,
|
||||||
false,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
for bb in body.basic_blocks.iter() {
|
for bb in body.basic_blocks.iter() {
|
||||||
|
@ -71,7 +77,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, in_drop: bool) -> McfResult {
|
fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
|
||||||
for arg in ty.walk() {
|
for arg in ty.walk() {
|
||||||
let ty = match arg.unpack() {
|
let ty = match arg.unpack() {
|
||||||
GenericArgKind::Type(ty) => ty,
|
GenericArgKind::Type(ty) => ty,
|
||||||
|
@ -81,27 +87,6 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, in_drop: bool) ->
|
||||||
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
|
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only do this check if we're in `TerminatorKind::Drop`, otherwise rustc will sometimes overflow
|
|
||||||
// its stack. This check is unnecessary outside of a `Drop` anyway so it's faster regardless
|
|
||||||
if in_drop && let ty::Adt(def, subst) = ty.kind() {
|
|
||||||
if def.has_non_const_dtor(tcx) && in_drop {
|
|
||||||
return Err((
|
|
||||||
span,
|
|
||||||
"cannot drop locals with a non constant destructor in const fn".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
for fields in def.variants().iter().map(|v| &v.fields) {
|
|
||||||
for field in fields {
|
|
||||||
check_ty(tcx, field.ty(tcx, subst), span, in_drop)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for field in def.all_fields() {
|
|
||||||
check_ty(tcx, field.ty(tcx, subst), span, in_drop)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
ty::Ref(_, _, hir::Mutability::Mut) => {
|
ty::Ref(_, _, hir::Mutability::Mut) => {
|
||||||
return Err((span, "mutable references in const fn are unstable".into()));
|
return Err((span, "mutable references in const fn are unstable".into()));
|
||||||
|
@ -288,6 +273,7 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
|
||||||
|
|
||||||
fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
|
fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
|
||||||
let mut cursor = place.projection.as_ref();
|
let mut cursor = place.projection.as_ref();
|
||||||
|
|
||||||
while let [ref proj_base @ .., elem] = *cursor {
|
while let [ref proj_base @ .., elem] = *cursor {
|
||||||
cursor = proj_base;
|
cursor = proj_base;
|
||||||
match elem {
|
match elem {
|
||||||
|
@ -327,20 +313,19 @@ fn check_terminator<'tcx>(
|
||||||
| TerminatorKind::Resume
|
| TerminatorKind::Resume
|
||||||
| TerminatorKind::Terminate
|
| TerminatorKind::Terminate
|
||||||
| TerminatorKind::Unreachable => Ok(()),
|
| TerminatorKind::Unreachable => Ok(()),
|
||||||
|
|
||||||
TerminatorKind::Drop { place, .. } => {
|
TerminatorKind::Drop { place, .. } => {
|
||||||
for local in &body.local_decls {
|
if !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body) {
|
||||||
check_ty(tcx, local.ty, span, true)?;
|
return Err((
|
||||||
|
span,
|
||||||
|
"cannot drop locals with a non constant destructor in const fn".into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
check_place(tcx, *place, span, body)
|
check_place(tcx, *place, span, body)
|
||||||
},
|
},
|
||||||
|
|
||||||
TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
|
TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
|
||||||
|
|
||||||
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
|
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
|
||||||
Err((span, "const fn generators are unstable".into()))
|
Err((span, "const fn generators are unstable".into()))
|
||||||
},
|
},
|
||||||
|
|
||||||
TerminatorKind::Call {
|
TerminatorKind::Call {
|
||||||
func,
|
func,
|
||||||
args,
|
args,
|
||||||
|
@ -384,7 +369,6 @@ fn check_terminator<'tcx>(
|
||||||
Err((span, "can only call other const fns within const fn".into()))
|
Err((span, "can only call other const fns within const fn".into()))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
TerminatorKind::Assert {
|
TerminatorKind::Assert {
|
||||||
cond,
|
cond,
|
||||||
expected: _,
|
expected: _,
|
||||||
|
@ -392,7 +376,6 @@ fn check_terminator<'tcx>(
|
||||||
target: _,
|
target: _,
|
||||||
unwind: _,
|
unwind: _,
|
||||||
} => check_operand(tcx, cond, span, body),
|
} => check_operand(tcx, cond, span, body),
|
||||||
|
|
||||||
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
|
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -406,8 +389,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
|
||||||
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
|
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
|
||||||
|
|
||||||
// HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver`
|
// HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver`
|
||||||
// doesn't accept the `-dev` version number so we have to strip it
|
// doesn't accept the `-dev` version number so we have to strip it off.
|
||||||
// off.
|
|
||||||
let short_version = since
|
let short_version = since
|
||||||
.as_str()
|
.as_str()
|
||||||
.split('-')
|
.split('-')
|
||||||
|
@ -425,3 +407,33 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool {
|
||||||
|
// Avoid selecting for simple cases, such as builtin types.
|
||||||
|
if ty::util::is_trivially_const_drop(ty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let obligation = Obligation::new(
|
||||||
|
tcx,
|
||||||
|
ObligationCause::dummy_with_span(body.span),
|
||||||
|
ConstCx::new(tcx, body).param_env.with_constness(Constness::Const),
|
||||||
|
TraitRef::from_lang_item(tcx, LangItem::Destruct, body.span, [ty]).with_constness(BoundConstness::ConstIfConst),
|
||||||
|
);
|
||||||
|
|
||||||
|
let fields_all_const_destruct = if let ty::Adt(def, subst) = ty.kind() && !ty.is_union() {
|
||||||
|
// This is such a mess even rustfmt doesn't wanna touch it
|
||||||
|
def.all_fields()
|
||||||
|
.map(|field| is_ty_const_destruct(tcx, field.ty(tcx, subst), body))
|
||||||
|
.all(|f| f)
|
||||||
|
&& def.variants().iter()
|
||||||
|
.map(|variant| variant.fields.iter().map(|field| is_ty_const_destruct(tcx, field.ty(tcx, subst), body)))
|
||||||
|
.all(|mut fs| fs.all(|f| f))
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
let infcx = tcx.infer_ctxt().build();
|
||||||
|
let mut selcx = SelectionContext::new(&infcx);
|
||||||
|
selcx.select(&obligation).is_ok() && fields_all_const_destruct
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ extern crate proc_macros;
|
||||||
|
|
||||||
use proc_macros::with_span;
|
use proc_macros::with_span;
|
||||||
|
|
||||||
struct Game;
|
struct Game; // You just lost.
|
||||||
|
|
||||||
// This should not be linted because it's already const
|
// This should not be linted because it's already const
|
||||||
const fn already_const() -> i32 {
|
const fn already_const() -> i32 {
|
||||||
|
@ -135,3 +135,5 @@ enum A {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn b(this: A) {}
|
fn b(this: A) {}
|
||||||
|
|
||||||
|
fn c(this: Vec<u16>) {}
|
||||||
|
|
Loading…
Reference in a new issue