diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 7727074f7b3..e0418be7aa6 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -384,9 +384,10 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { NONMINIMAL_BOOL, e.span, "this boolean expression can be simplified", - |db| for suggestion in &improvements { - db.span_suggestion(e.span, "try", suggest(self.cx, suggestion, &h2q.terminals)); - break; // FIXME: multiple suggestions in rustc are broken + |db| { + db.span_suggestions(e.span, "try", improvements.into_iter().map(|suggestion| { + suggest(self.cx, suggestion, &h2q.terminals) + }).collect()); }); } } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 1710871a31f..e9fd654eebf 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -136,7 +136,8 @@ fn check_hash_peq<'a, 'tcx>( /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item, trait_ref: &TraitRef, ty: ty::Ty<'tcx>) { if match_path_old(&trait_ref.path, &paths::CLONE_TRAIT) { - if !is_copy(cx, ty, item.id) { + let def_id = cx.tcx.hir.local_def_id(item.id); + if !is_copy(cx, ty, def_id) { return; } diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 688f1cc9eb0..e5064febe95 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -136,7 +136,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { &msg, arg.span, &format!("argument has type {}", arg_ty.sty)); - } else if is_copy(cx, arg_ty, cx.tcx.hir.get_parent(arg.id)) { + } else if is_copy(cx, arg_ty, cx.tcx.hir.local_def_id(cx.tcx.hir.get_parent(arg.id))) { if match_def_path(cx.tcx, def_id, &paths::DROP) { lint = DROP_COPY; msg = DROP_COPY_SUMMARY.to_string(); diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index b2183ff1c73..894ba09f50f 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -76,6 +76,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp { BiLt | BiLe | BiGe | BiGt => (cx.tcx.lang_items.ord_trait(), true), }; let parent = cx.tcx.hir.get_parent(e.id); + let parent = cx.tcx.hir.local_def_id(parent); if let Some(trait_id) = trait_id { #[allow(match_same_arms)] match (&left.node, &right.node) { diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c54a82984f1..5e49fb609d1 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -873,7 +873,7 @@ impl<'tcx> Visitor<'tcx> for UsedVisitor { struct VarVisitor<'a, 'tcx: 'a> { cx: &'a LateContext<'a, 'tcx>, // context reference var: DefId, // var name to look for as index - indexed: HashMap>>, // indexed variables, the extent is None for global + indexed: HashMap>, // indexed variables, the extent is None for global nonindex: bool, // has the var been used otherwise? } diff --git a/clippy_lints/src/methods.rs b/clippy_lints/src/methods.rs index 64a8f72ea01..738350a4af9 100644 --- a/clippy_lints/src/methods.rs +++ b/clippy_lints/src/methods.rs @@ -657,8 +657,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { } // check conventions w.r.t. conversion method names and predicates - let ty = cx.tcx.type_of(cx.tcx.hir.local_def_id(item.id)); - let is_copy = is_copy(cx, ty, item.id); + let def_id = cx.tcx.hir.local_def_id(item.id); + let ty = cx.tcx.type_of(def_id); + let is_copy = is_copy(cx, ty, def_id); for &(ref conv, self_kinds) in &CONVENTIONS { if_let_chain! {[ conv.check(&name.as_str()), @@ -683,8 +684,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { } let ret_ty = return_ty(cx, implitem.id); + let implitem_defid = cx.tcx.hir.local_def_id(implitem.id); if name == "new" && - !ret_ty.walk().any(|t| same_tys(cx, t, ty, implitem.id)) { + !ret_ty.walk().any(|t| same_tys(cx, t, ty, implitem_defid)) { span_lint(cx, NEW_RET_NO_SELF, implitem.span, @@ -837,7 +839,7 @@ fn lint_clone_on_copy(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr, arg_t } } - if is_copy(cx, ty, parent) { + if is_copy(cx, ty, cx.tcx.hir.local_def_id(parent)) { span_lint_and_then(cx, CLONE_ON_COPY, expr.span, diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index dc5c34e33eb..b5791ad0c19 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -369,11 +369,11 @@ impl MiscEarly { "if you mean to use a decimal constant, remove the `0` to remove confusion:", src[1..].to_string(), ); - /*db.span_suggestion( + db.span_suggestion( lit.span, "if you mean to use an octal constant, use `0o`:", format!("0o{}", &src[1..]), - ); FIXME: rustc doesn't support multiple suggestions anymore */ + ); }); } }} diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 6cc53c52f72..afcf6f22a4f 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -79,15 +79,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { let asref_trait = need!(get_trait_def_id(cx, &paths::ASREF_TRAIT)); let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT)); + let fn_def_id = cx.tcx.hir.local_def_id(node_id); + let preds: Vec = { - let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id); + let parameter_env = cx.tcx.parameter_environment(fn_def_id); traits::elaborate_predicates(cx.tcx, parameter_env.caller_bounds.to_vec()) .filter(|p| !p.is_global()) .collect() }; - let fn_def_id = cx.tcx.hir.local_def_id(node_id); - // Collect moved variables and spans which will need dereferencings from the function body. let MovedVariablesCtxt { moved_vars, spans_need_deref, .. } = { let mut ctx = MovedVariablesCtxt::new(cx); @@ -100,9 +100,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { ctx }; - let param_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id); let fn_sig = cx.tcx.type_of(fn_def_id).fn_sig(); - let fn_sig = cx.tcx.liberate_late_bound_regions(param_env.free_id_outlive, &fn_sig); + let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig); for ((input, &ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) { @@ -120,7 +119,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { if_let_chain! {[ !is_self(arg), !ty.is_mutable_pointer(), - !is_copy(cx, ty, node_id), + !is_copy(cx, ty, fn_def_id), !implements_trait(cx, ty, fn_trait, &[], Some(node_id)), !implements_trait(cx, ty, asref_trait, &[], Some(node_id)), !implements_borrow_trait, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 7087ec698f4..0abb639d59a 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -108,12 +108,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { // can't be implemented by default return; } + let def_id = cx.tcx.hir.local_def_id(id); if decl.inputs.is_empty() && name == "new" && cx.access_levels.is_reachable(id) { let self_ty = cx.tcx .type_of(cx.tcx.hir.local_def_id(cx.tcx.hir.get_parent(id))); if_let_chain!{[ self_ty.walk_shallow().next().is_none(), // implements_trait does not work with generics - same_tys(cx, self_ty, return_ty(cx, id), id), + same_tys(cx, self_ty, return_ty(cx, id), def_id), let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT), !implements_trait(cx, self_ty, default_trait_id, &[], None) ], { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2c1043cffa0..490b447850a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -7,7 +7,7 @@ use rustc::lint::{LintContext, LateContext, Level, Lint}; use rustc::session::Session; use rustc::traits::Reveal; use rustc::traits; -use rustc::ty::subst::Subst; +use rustc::ty::subst::{Subst, Substs}; use rustc::ty; use rustc::ty::layout::TargetDataLayout; use rustc::mir::transform::MirSource; @@ -18,7 +18,7 @@ use std::mem; use std::str::FromStr; use syntax::ast::{self, LitKind}; use syntax::attr; -use syntax::codemap::{ExpnFormat, ExpnInfo, MultiSpan, Span, DUMMY_SP}; +use syntax::codemap::{ExpnFormat, ExpnInfo, Span, DUMMY_SP}; use syntax::errors::DiagnosticBuilder; use syntax::ptr::P; use syntax::symbol::keywords; @@ -594,12 +594,10 @@ pub fn span_lint_and_sugg<'a, 'tcx: 'a, T: LintContext<'tcx>>( /// replacement. In human-readable format though, it only appears once before the whole suggestion. pub fn multispan_sugg(db: &mut DiagnosticBuilder, help_msg: String, sugg: Vec<(Span, String)>) { let sugg = rustc_errors::CodeSuggestion { - msp: MultiSpan::from_spans(sugg.iter().map(|&(span, _)| span).collect()), - substitutes: sugg.into_iter().map(|(_, subs)| subs).collect(), + substitution_parts: sugg.into_iter().map(|(span, sub)| rustc_errors::Substitution { span, substitutions: vec![sub] }).collect(), msg: help_msg, }; - assert!(db.suggestion.is_none()); - db.suggestion = Some(sugg); + db.suggestions.push(sugg); } /// Return the base type for references and raw pointers. @@ -777,11 +775,9 @@ pub fn camel_case_from(s: &str) -> usize { /// Convenience function to get the return type of a function pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: NodeId) -> ty::Ty<'tcx> { - let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_item); let fn_def_id = cx.tcx.hir.local_def_id(fn_item); - let fn_sig = cx.tcx.type_of(fn_def_id).fn_sig(); - let fn_sig = cx.tcx.liberate_late_bound_regions(parameter_env.free_id_outlive, &fn_sig); - fn_sig.output() + let ret_ty = cx.tcx.type_of(fn_def_id).fn_sig().output(); + cx.tcx.erase_late_bound_regions(&ret_ty) } /// Check if two types are the same. @@ -791,12 +787,13 @@ pub fn same_tys<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, a: ty::Ty<'tcx>, b: ty::Ty<'tcx>, - parameter_item: NodeId + parameter_item: DefId ) -> bool { - let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, parameter_item); + let parameter_env = cx.tcx.parameter_environment(parameter_item); cx.tcx.infer_ctxt(parameter_env, Reveal::All).enter(|infcx| { - let new_a = a.subst(infcx.tcx, infcx.parameter_environment.free_substs); - let new_b = b.subst(infcx.tcx, infcx.parameter_environment.free_substs); + let substs = Substs::identity_for_item(cx.tcx, parameter_item); + let new_a = a.subst(infcx.tcx, substs); + let new_b = b.subst(infcx.tcx, substs); infcx.can_equate(&new_a, &new_b).is_ok() }) } @@ -810,9 +807,10 @@ pub fn type_is_unsafe_function(ty: ty::Ty) -> bool { } } -pub fn is_copy<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, env: NodeId) -> bool { - let env = ty::ParameterEnvironment::for_item(cx.tcx, env); - !ty.subst(cx.tcx, env.free_substs).moves_by_default(cx.tcx.global_tcx(), &env, DUMMY_SP) +pub fn is_copy<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, env: DefId) -> bool { + let substs = Substs::identity_for_item(cx.tcx, env); + let env = cx.tcx.parameter_environment(env); + !ty.subst(cx.tcx, substs).moves_by_default(cx.tcx.global_tcx(), &env, DUMMY_SP) } /// Return whether a pattern is refutable. diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index da65474ed96..e162406b8dc 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { if_let_chain!{[ let Some((_, arg, _)) = higher::for_loop(expr), let Some(vec_args) = higher::vec_macro(cx, arg), - is_copy(cx, vec_type(cx.tables.expr_ty_adjusted(arg)), cx.tcx.hir.get_parent(expr.id)), + is_copy(cx, vec_type(cx.tables.expr_ty_adjusted(arg)), cx.tcx.hir.local_def_id(cx.tcx.hir.get_parent(expr.id))), ], { // report the error around the `vec!` not inside `:` let span = arg.span.ctxt.outer().expn_info().map(|info| info.call_site).expect("unable to get call_site"); diff --git a/tests/ui/booleans.stderr b/tests/ui/booleans.stderr index 27e1d882a7b..54a09c97874 100644 --- a/tests/ui/booleans.stderr +++ b/tests/ui/booleans.stderr @@ -79,13 +79,21 @@ error: this boolean expression can be simplified --> $DIR/booleans.rs:34:13 | 34 | let _ = a == b && c == 5 && a == b; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `a == b && c == 5` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | let _ = a == b && c == 5; + | let _ = !(c != 5 || a != b); error: this boolean expression can be simplified --> $DIR/booleans.rs:35:13 | 35 | let _ = a == b && c == 5 && b == a; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `a == b && c == 5` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | let _ = a == b && c == 5; + | let _ = !(c != 5 || a != b); error: this boolean expression contains a logic bug --> $DIR/booleans.rs:36:13 @@ -115,7 +123,11 @@ error: this boolean expression can be simplified --> $DIR/booleans.rs:39:13 | 39 | let _ = a != b || !(a != b || c == d); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `c != d || a != b` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | let _ = c != d || a != b; + | let _ = !(a == b && c == d); error: aborting due to 13 previous errors diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index e87fe887bf4..b9a0fc04181 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -71,6 +71,8 @@ note: lint level defined here | ^^^^^^^^^^^^^^^^^^^^^ help: if you mean to use a decimal constant, remove the `0` to remove confusion: | let fail8 = 123; +help: if you mean to use an octal constant, use `0o`: + | let fail8 = 0o123; error: aborting due to 9 previous errors diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 24df4607b19..e529f6c8e2f 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -11,7 +11,6 @@ note: lint level defined here | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this | #[derive(Default)] - | pub struct Foo; error: you should consider deriving a `Default` implementation for `Bar` --> $DIR/new_without_default.rs:16:5 @@ -21,7 +20,6 @@ error: you should consider deriving a `Default` implementation for `Bar` | help: try this | #[derive(Default)] - | pub struct Bar; error: you should consider adding a `Default` implementation for `LtKo<'c>` --> $DIR/new_without_default.rs:64:5