rustc_lint: handle more method calls in unconditional_recursion.
This commit is contained in:
parent
a34b0a4691
commit
585f0e928b
@ -33,7 +33,6 @@
|
||||
|
||||
use metadata::{csearch, decoder};
|
||||
use middle::{cfg, def, infer, pat_util, stability, traits};
|
||||
use middle::def::*;
|
||||
use middle::subst::Substs;
|
||||
use middle::ty::{self, Ty};
|
||||
use middle::const_eval::{eval_const_expr_partial, ConstVal};
|
||||
@ -2251,34 +2250,73 @@ impl LintPass for UnconditionalRecursion {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the method call `id` refers to method `method`.
|
||||
// Check if the expression `id` performs a call to `method`.
|
||||
fn expr_refers_to_this_method(tcx: &ty::ctxt,
|
||||
method: &ty::Method,
|
||||
id: ast::NodeId) -> bool {
|
||||
let method_call = ty::MethodCall::expr(id);
|
||||
let callee = match tcx.tables.borrow().method_map.get(&method_call) {
|
||||
Some(&m) => m,
|
||||
None => return false
|
||||
};
|
||||
let callee_item = tcx.impl_or_trait_item(callee.def_id);
|
||||
let tables = tcx.tables.borrow();
|
||||
|
||||
// Check for method calls and overloaded operators.
|
||||
if let Some(m) = tables.method_map.get(&ty::MethodCall::expr(id)) {
|
||||
if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for overloaded autoderef method calls.
|
||||
if let Some(&ty::AdjustDerefRef(ref adj)) = tables.adjustments.get(&id) {
|
||||
for i in 0..adj.autoderefs {
|
||||
let method_call = ty::MethodCall::autoderef(id, i as u32);
|
||||
if let Some(m) = tables.method_map.get(&method_call) {
|
||||
if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for calls to methods via explicit paths (e.g. `T::method()`).
|
||||
match tcx.map.get(id) {
|
||||
ast_map::NodeExpr(&ast::Expr { node: ast::ExprCall(ref callee, _), .. }) => {
|
||||
match tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def()) {
|
||||
Some(def::DefMethod(def_id)) => {
|
||||
let no_substs = &ty::ItemSubsts::empty();
|
||||
let ts = tables.item_substs.get(&callee.id).unwrap_or(no_substs);
|
||||
method_call_refers_to_method(tcx, method, def_id, &ts.substs, id)
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the method call to the method with the ID `callee_id`
|
||||
// and instantiated with `callee_substs` refers to method `method`.
|
||||
fn method_call_refers_to_method<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||
method: &ty::Method,
|
||||
callee_id: ast::DefId,
|
||||
callee_substs: &Substs<'tcx>,
|
||||
expr_id: ast::NodeId) -> bool {
|
||||
let callee_item = tcx.impl_or_trait_item(callee_id);
|
||||
|
||||
match callee_item.container() {
|
||||
// This is an inherent method, so the `def_id` refers
|
||||
// directly to the method definition.
|
||||
ty::ImplContainer(_) => {
|
||||
callee.def_id == method.def_id
|
||||
callee_id == method.def_id
|
||||
}
|
||||
|
||||
// A trait method, from any number of possible sources.
|
||||
// Attempt to select a concrete impl before checking.
|
||||
ty::TraitContainer(trait_def_id) => {
|
||||
let trait_substs = callee.substs.clone().method_to_trait();
|
||||
let trait_substs = callee_substs.clone().method_to_trait();
|
||||
let trait_substs = tcx.mk_substs(trait_substs);
|
||||
let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs);
|
||||
let trait_ref = ty::Binder(trait_ref);
|
||||
let span = tcx.map.span(id);
|
||||
let span = tcx.map.span(expr_id);
|
||||
let obligation =
|
||||
traits::Obligation::new(traits::ObligationCause::misc(span, id),
|
||||
traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
|
||||
trait_ref.to_poly_trait_predicate());
|
||||
|
||||
let param_env = ty::ParameterEnvironment::for_item(tcx, method.def_id.node);
|
||||
@ -2289,12 +2327,12 @@ impl LintPass for UnconditionalRecursion {
|
||||
// If `T` is `Self`, then this call is inside
|
||||
// a default method definition.
|
||||
Ok(Some(traits::VtableParam(_))) => {
|
||||
let self_ty = callee.substs.self_ty();
|
||||
let self_ty = callee_substs.self_ty();
|
||||
let on_self = self_ty.map_or(false, |t| t.is_self());
|
||||
// We can only be recurring in a default
|
||||
// method if we're being called literally
|
||||
// on the `Self` type.
|
||||
on_self && callee.def_id == method.def_id
|
||||
on_self && callee_id == method.def_id
|
||||
}
|
||||
|
||||
// The `impl` is known, so we check that with a
|
||||
@ -2454,7 +2492,7 @@ impl LintPass for MutableTransmutes {
|
||||
ast::ExprPath(..) => (),
|
||||
_ => return None
|
||||
}
|
||||
if let DefFn(did, _) = cx.tcx.resolve_expr(expr) {
|
||||
if let def::DefFn(did, _) = cx.tcx.resolve_expr(expr) {
|
||||
if !def_id_is_transmute(cx, did) {
|
||||
return None;
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ fn quz() -> bool { //~ ERROR function cannot return without recurring
|
||||
}
|
||||
}
|
||||
|
||||
// Trait method calls.
|
||||
trait Foo {
|
||||
fn bar(&self) { //~ ERROR function cannot return without recurring
|
||||
self.bar() //~ NOTE recursive call site
|
||||
@ -53,14 +54,79 @@ impl Foo for Box<Foo+'static> {
|
||||
self.bar() //~ NOTE recursive call site
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trait method call with integer fallback after method resolution.
|
||||
impl Foo for i32 {
|
||||
fn bar(&self) { //~ ERROR function cannot return without recurring
|
||||
0.bar() //~ NOTE recursive call site
|
||||
}
|
||||
}
|
||||
|
||||
impl Foo for u32 {
|
||||
fn bar(&self) {
|
||||
0.bar()
|
||||
}
|
||||
}
|
||||
|
||||
// Trait method calls via paths.
|
||||
trait Foo2 {
|
||||
fn bar(&self) { //~ ERROR function cannot return without recurring
|
||||
Foo2::bar(self) //~ NOTE recursive call site
|
||||
}
|
||||
}
|
||||
|
||||
impl Foo2 for Box<Foo2+'static> {
|
||||
fn bar(&self) { //~ ERROR function cannot return without recurring
|
||||
loop {
|
||||
Foo2::bar(self) //~ NOTE recursive call site
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Baz;
|
||||
impl Baz {
|
||||
// Inherent method call.
|
||||
fn qux(&self) { //~ ERROR function cannot return without recurring
|
||||
self.qux(); //~ NOTE recursive call site
|
||||
}
|
||||
|
||||
// Inherent method call via path.
|
||||
fn as_ref(&self) -> &Self { //~ ERROR function cannot return without recurring
|
||||
Baz::as_ref(self) //~ NOTE recursive call site
|
||||
}
|
||||
}
|
||||
|
||||
// Trait method calls to impls via paths.
|
||||
impl Default for Baz {
|
||||
fn default() -> Baz { //~ ERROR function cannot return without recurring
|
||||
let x = Default::default(); //~ NOTE recursive call site
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
// Overloaded operators.
|
||||
impl std::ops::Deref for Baz {
|
||||
type Target = ();
|
||||
fn deref(&self) -> &() { //~ ERROR function cannot return without recurring
|
||||
&**self //~ NOTE recursive call site
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<usize> for Baz {
|
||||
type Output = Baz;
|
||||
fn index(&self, x: usize) -> &Baz { //~ ERROR function cannot return without recurring
|
||||
&self[x] //~ NOTE recursive call site
|
||||
}
|
||||
}
|
||||
|
||||
// Overloaded autoderef.
|
||||
struct Quux;
|
||||
impl std::ops::Deref for Quux {
|
||||
type Target = Baz;
|
||||
fn deref(&self) -> &Baz { //~ ERROR function cannot return without recurring
|
||||
self.as_ref() //~ NOTE recursive call site
|
||||
}
|
||||
}
|
||||
|
||||
fn all_fine() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user