diff --git a/CHANGELOG.md b/CHANGELOG.md index d1dfe36ffd8..617bf32f463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1559,6 +1559,7 @@ Released 2018-09-13 [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons @@ -1634,6 +1635,8 @@ Released 2018-09-13 [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string [`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display [`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic @@ -1644,6 +1647,7 @@ Released 2018-09-13 [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop @@ -1919,6 +1923,5 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr -[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/README.md b/README.md index a2984d73641..62a8be0abf2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs new file mode 100644 index 00000000000..ef1f1a14afc --- /dev/null +++ b/clippy_lints/src/asm_syntax.rs @@ -0,0 +1,125 @@ +use std::fmt; + +use crate::utils::span_lint_and_help; +use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum AsmStyle { + Intel, + Att, +} + +impl fmt::Display for AsmStyle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AsmStyle::Intel => f.write_str("Intel"), + AsmStyle::Att => f.write_str("AT&T"), + } + } +} + +impl std::ops::Not for AsmStyle { + type Output = AsmStyle; + + fn not(self) -> AsmStyle { + match self { + AsmStyle::Intel => AsmStyle::Att, + AsmStyle::Att => AsmStyle::Intel, + } + } +} + +fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr, check_for: AsmStyle) { + if let ExprKind::InlineAsm(ref inline_asm) = expr.kind { + let style = if inline_asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { + AsmStyle::Att + } else { + AsmStyle::Intel + }; + + if style == check_for { + span_lint_and_help( + cx, + lint, + expr.span, + &format!("{} x86 assembly syntax used", style), + None, + &format!("use {} x86 assembly syntax", !style), + ); + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of Intel x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for AT&T x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + pub INLINE_ASM_X86_INTEL_SYNTAX, + restriction, + "prefer AT&T x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86IntelSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Intel); + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of AT&T x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for Intel x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + pub INLINE_ASM_X86_ATT_SYNTAX, + restriction, + "prefer Intel x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86AttSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Att); + } +} diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs new file mode 100644 index 00000000000..581c3242e37 --- /dev/null +++ b/clippy_lints/src/disallowed_method.rs @@ -0,0 +1,73 @@ +use crate::utils::span_lint; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Symbol; + +declare_clippy_lint! { + /// **What it does:** Lints for specific trait methods defined in clippy.toml + /// + /// **Why is this bad?** Some methods are undesirable in certain contexts, + /// and it would be beneficial to lint for them as needed. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// // example code where clippy issues a warning + /// foo.bad_method(); // Foo::bad_method is disallowed in the configuration + /// ``` + /// Use instead: + /// ```rust,ignore + /// // example code which does not raise clippy warning + /// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed + /// ``` + pub DISALLOWED_METHOD, + nursery, + "use of a disallowed method call" +} + +#[derive(Clone, Debug)] +pub struct DisallowedMethod { + disallowed: FxHashSet>, +} + +impl DisallowedMethod { + pub fn new(disallowed: &FxHashSet) -> Self { + Self { + disallowed: disallowed + .iter() + .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) + .collect(), + } + } +} + +impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); + +impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind { + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + + let method_call = cx.get_def_path(def_id); + if self.disallowed.contains(&method_call) { + let method = method_call + .iter() + .map(|s| s.to_ident_string()) + .collect::>() + .join("::"); + + span_lint( + cx, + DISALLOWED_METHOD, + expr.span, + &format!("use of a disallowed method `{}`", method), + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 70efdaeb9c6..72ad8d12e6d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -153,6 +153,7 @@ mod utils; mod approx_const; mod arithmetic; mod as_conversions; +mod asm_syntax; mod assertions_on_constants; mod assign_ops; mod async_yields_async; @@ -176,6 +177,7 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; +mod disallowed_method; mod doc; mod double_comparison; mod double_parens; @@ -489,6 +491,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, &as_conversions::AS_CONVERSIONS, + &asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, + &asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, @@ -529,6 +533,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, + &disallowed_method::DISALLOWED_METHOD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -851,9 +856,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::UNIT_CMP, &types::UNNECESSARY_CAST, &types::VEC_BOX, + &unicode::INVISIBLE_CHARACTERS, &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, - &unicode::ZERO_WIDTH_SPACE, &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, @@ -1120,11 +1125,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); + let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); + store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); + store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); + store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), + LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), @@ -1499,7 +1511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNIT_CMP), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1592,6 +1604,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&panic_unimplemented::PANIC_PARAMS), @@ -1747,8 +1761,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), - LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), @@ -1766,7 +1778,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1807,6 +1819,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(&disallowed_method::DISALLOWED_METHOD), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), @@ -1896,6 +1909,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); + ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 4df6827d77f..d7043e7bd8f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,21 +1,22 @@ +use crate::utils::paths; +use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, + NestedVisitorMap, Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, - ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, - TyKind, WhereClause, WherePredicate, + BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, + ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, + TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; - -use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; +use std::iter::FromIterator; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -25,8 +26,11 @@ declare_clippy_lint! { /// complicated, while there is nothing out of the ordinary going on. Removing /// them leads to more readable code. /// - /// **Known problems:** Potential false negatives: we bail out if the function - /// has a `where` clause where lifetimes are mentioned. + /// **Known problems:** + /// - We bail out if the function has a `where` clause where lifetimes + /// are mentioned due to potenial false positives. + /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the + /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. /// /// **Example:** /// ```rust @@ -108,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } /// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq, Eq, Hash, Debug, Clone)] enum RefLt { Unnamed, Static, @@ -127,7 +131,6 @@ fn check_fn_inner<'tcx>( return; } - let mut bounds_lts = Vec::new(); let types = generics .params .iter() @@ -156,13 +159,12 @@ fn check_fn_inner<'tcx>( if bound.name != LifetimeName::Static && !bound.is_elided() { return; } - bounds_lts.push(bound); } } } } } - if could_use_elision(cx, decl, body, &generics.params, bounds_lts) { + if could_use_elision(cx, decl, body, &generics.params) { span_lint( cx, NEEDLESS_LIFETIMES, @@ -181,7 +183,6 @@ fn could_use_elision<'tcx>( func: &'tcx FnDecl<'_>, body: Option, named_generics: &'tcx [GenericParam<'_>], - bounds_lts: Vec<&'tcx Lifetime>, ) -> bool { // There are two scenarios where elision works: // * no output references, all input references have different LT @@ -204,15 +205,31 @@ fn could_use_elision<'tcx>( if let Return(ref ty) = func.output { output_visitor.visit_ty(ty); } + for lt in named_generics { + input_visitor.visit_generic_param(lt) + } - let input_lts = match input_visitor.into_vec() { - Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()), - None => return false, - }; - let output_lts = match output_visitor.into_vec() { - Some(val) => val, - None => return false, - }; + if input_visitor.abort() || output_visitor.abort() { + return false; + } + + if allowed_lts + .intersection(&FxHashSet::from_iter( + input_visitor + .nested_elision_site_lts + .iter() + .chain(output_visitor.nested_elision_site_lts.iter()) + .cloned() + .filter(|v| matches!(v, RefLt::Named(_))), + )) + .next() + .is_some() + { + return false; + } + + let input_lts = input_visitor.lts; + let output_lts = output_visitor.lts; if let Some(body_id) = body { let mut checker = BodyLifetimeChecker { @@ -277,27 +294,20 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { allowed_lts } -fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bounds_lts: T) -> Vec { - for lt in bounds_lts { - if lt.name != LifetimeName::Static { - vec.push(RefLt::Named(lt.name.ident().name)); - } - } - - vec -} - /// Number of unique lifetimes in the given vector. #[must_use] fn unique_lifetimes(lts: &[RefLt]) -> usize { lts.iter().collect::>().len() } +const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; + /// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, lts: Vec, - abort: bool, + nested_elision_site_lts: Vec, + unelided_trait_object_lifetime: bool, } impl<'a, 'tcx> RefVisitor<'a, 'tcx> { @@ -305,7 +315,8 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), - abort: false, + nested_elision_site_lts: Vec::new(), + unelided_trait_object_lifetime: false, } } @@ -325,40 +336,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { } } - fn into_vec(self) -> Option> { - if self.abort { - None - } else { - Some(self.lts) - } + fn all_lts(&self) -> Vec { + self.lts + .iter() + .chain(self.nested_elision_site_lts.iter()) + .cloned() + .collect::>() } - fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { - if let Some(ref last_path_segment) = last_path_segment(qpath).args { - if !last_path_segment.parenthesized - && !last_path_segment - .args - .iter() - .any(|arg| matches!(arg, GenericArg::Lifetime(_))) - { - let hir_id = ty.hir_id; - match self.cx.qpath_res(qpath, hir_id) { - Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => { - let generics = self.cx.tcx.generics_of(def_id); - for _ in generics.params.as_slice() { - self.record(&None); - } - }, - Res::Def(DefKind::Trait, def_id) => { - let trait_def = self.cx.tcx.trait_def(def_id); - for _ in &self.cx.tcx.generics_of(trait_def.def_id).params { - self.record(&None); - } - }, - _ => (), - } - } - } + fn abort(&self) -> bool { + self.unelided_trait_object_lifetime } } @@ -370,30 +357,37 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { self.record(&Some(*lifetime)); } + fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) { + let trait_ref = &poly_tref.trait_ref; + if CLOSURE_TRAIT_BOUNDS + .iter() + .any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) + { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_trait_ref(trait_ref); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + } else { + walk_poly_trait_ref(self, poly_tref, tbm); + } + } + fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { match ty.kind { - TyKind::Rptr(ref lt, _) if lt.is_elided() => { - self.record(&None); - }, - TyKind::Path(ref path) => { - self.collect_anonymous_lifetimes(path, ty); - }, TyKind::OpaqueDef(item, _) => { let map = self.cx.tcx.hir(); - if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind { - for bound in exist_ty.bounds { - if let GenericBound::Outlives(_) = *bound { - self.record(&None); - } - } - } else { - unreachable!() - } + let item = map.expect_item(item.id); + walk_item(self, item); walk_ty(self, ty); }, + TyKind::BareFn(&BareFnTy { decl, .. }) => { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_fn_decl(decl); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + return; + }, TyKind::TraitObject(bounds, ref lt) => { if !lt.is_elided() { - self.abort = true; + self.unelided_trait_object_lifetime = true; } for bound in bounds { self.visit_poly_trait_ref(bound, TraitBoundModifier::None); @@ -430,16 +424,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - match visitor.into_vec() { - None => return false, - Some(lts) => { - for lt in lts { - if !allowed_lts.contains(<) { - return true; - } - } - }, - } + return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)); }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3410341a1e3..61b63597b16 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, + contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, @@ -1276,6 +1276,8 @@ fn check_for_loop_range<'tcx>( let skip = if starts_at_zero { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { + return; } else { format!(".skip({})", snippet(cx, start.span, "..")) }; @@ -1302,6 +1304,8 @@ fn check_for_loop_range<'tcx>( if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + return; } else { match limits { ast::RangeLimits::Closed => { @@ -2134,7 +2138,7 @@ enum VarState { DontWarn, } -/// Scan a for loop for variables that are incremented exactly once. +/// Scan a for loop for variables that are incremented exactly once and not used after that. struct IncrementVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference states: FxHashMap, // incremented variables @@ -2154,6 +2158,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { if let Some(def_id) = var_def_id(self.cx, expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { let state = self.states.entry(def_id).or_insert(VarState::Initial); + if *state == VarState::IncrOnce { + *state = VarState::DontWarn; + return; + } match parent.kind { ExprKind::AssignOp(op, ref lhs, ref rhs) => { diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 80da04fb7de..25245b3dbf0 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,10 +1,10 @@ +use crate::utils::qualify_min_const_fn::is_min_const_fn; use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use crate::utils::qualify_min_const_fn::is_min_const_fn; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 38bdd0f7ed2..7687962bdd9 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use crate::utils::{in_macro, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; @@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod if let [segment] = &path.segments[..]; if segment.ident.name == kw::SelfUpper; then { + // In case we have a named lifetime, we check if the name comes from expansion. + // If it does, at this point we know the rest of the parameter was written by the user, + // so let them decide what the name of the lifetime should be. + // See #6089 for more details. + let mut applicability = Applicability::MachineApplicable; let self_param = match (binding_mode, mutbl) { (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ mut self".to_string() + } else { + format!("&{} mut self", &lifetime.ident.name) + } + }, (Mode::Ref(None), Mutability::Not) => "&self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Not) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ self".to_string() + } else { + format!("&{} self", &lifetime.ident.name) + } + }, (Mode::Value, Mutability::Mut) => "mut self".to_string(), (Mode::Value, Mutability::Not) => "self".to_string(), }; @@ -85,7 +104,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod "the type of the `self` parameter does not need to be arbitrary", "consider to change this parameter to", self_param, - Applicability::MachineApplicable, + applicability, ) } } @@ -93,7 +112,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod impl EarlyLintPass for NeedlessArbitrarySelfType { fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { - if !p.is_self() { + // Bail out if the parameter it's not a receiver or was not written by the user + if !p.is_self() || in_macro(p.span) { return; } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index bb44eeb6adc..7b662eae775 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -1,6 +1,6 @@ //! Checks for uses of const which the type is not `Freeze` (`Cell`-free). //! -//! This lint is **deny** by default. +//! This lint is **warn** by default. use std::ptr; @@ -17,6 +17,8 @@ use rustc_typeck::hir_ty_to_ty; use crate::utils::{in_constant, qpath_res, span_lint_and_then}; use if_chain::if_chain; +// FIXME: this is a correctness problem but there's no suitable +// warn-by-default category. declare_clippy_lint! { /// **What it does:** Checks for declaration of `const` items which is interior /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). @@ -34,6 +36,15 @@ declare_clippy_lint! { /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, /// and this lint should be suppressed. /// + /// When an enum has variants with interior mutability, use of its non interior mutable + /// variants can generate false positives. See issue + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// + /// Types that have underlying or potential interior mutability trigger the lint whether + /// the interior mutable field is used or not. See issues + /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) + /// /// **Example:** /// ```rust /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; @@ -49,10 +60,12 @@ declare_clippy_lint! { /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// ``` pub DECLARE_INTERIOR_MUTABLE_CONST, - correctness, + style, "declaring `const` with interior mutability" } +// FIXME: this is a correctness problem but there's no suitable +// warn-by-default category. declare_clippy_lint! { /// **What it does:** Checks if `const` items which is interior mutable (e.g., /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. @@ -64,7 +77,14 @@ declare_clippy_lint! { /// /// The `const` value should be stored inside a `static` item. /// - /// **Known problems:** None + /// **Known problems:** When an enum has variants with interior mutability, use of its non + /// interior mutable variants can generate false positives. See issue + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// + /// Types that have underlying or potential interior mutability trigger the lint whether + /// the interior mutable field is used or not. See issues + /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) /// /// **Example:** /// ```rust @@ -81,7 +101,7 @@ declare_clippy_lint! { /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// ``` pub BORROW_INTERIOR_MUTABLE_CONST, - correctness, + style, "referencing `const` with interior mutability" } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index a29a199b8c3..17d950169fd 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -216,18 +216,19 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for Rc and Arc when T is a mutable buffer type such as String or Vec + /// **What it does:** Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`. /// - /// **Why is this bad?** Expressions such as Rc have no advantage over Rc, since - /// it is larger and involves an extra level of indirection, and doesn't implement Borrow. + /// **Why is this bad?** Expressions such as `Rc` usually have no advantage over `Rc`, since + /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow`. /// - /// While mutating a buffer type would still be possible with Rc::get_mut(), it only - /// works if there are no additional references yet, which defeats the purpose of + /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only + /// works if there are no additional references yet, which usually defeats the purpose of /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner - /// type with an interior mutable container (such as RefCell or Mutex) would normally + /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally /// be used. /// - /// **Known problems:** None. + /// **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for + /// cases where mutation only happens before there are any additional references. /// /// **Example:** /// ```rust,ignore diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d8c57f0e7ae..93d59cc7fcd 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -8,18 +8,18 @@ use rustc_span::source_map::Span; use unicode_normalization::UnicodeNormalization; declare_clippy_lint! { - /// **What it does:** Checks for the Unicode zero-width space in the code. + /// **What it does:** Checks for invisible Unicode characters in the code. /// /// **Why is this bad?** Having an invisible character in the code makes for all /// sorts of April fools, but otherwise is very much frowned upon. /// /// **Known problems:** None. /// - /// **Example:** You don't see it, but there may be a zero-width space - /// somewhere in this text. - pub ZERO_WIDTH_SPACE, + /// **Example:** You don't see it, but there may be a zero-width space or soft hyphen + /// some­where in this text. + pub INVISIBLE_CHARACTERS, correctness, - "using a zero-width space in a string literal, which is confusing" + "using an invisible character in a string literal, which is confusing" } declare_clippy_lint! { @@ -63,7 +63,7 @@ declare_clippy_lint! { "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)" } -declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); +declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { @@ -91,14 +91,17 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if string.contains('\u{200B}') { + if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) { span_lint_and_sugg( cx, - ZERO_WIDTH_SPACE, + INVISIBLE_CHARACTERS, span, - "zero-width space detected", + "invisible character detected", "consider replacing the string with", - string.replace("\u{200B}", "\\u{200B}"), + string + .replace("\u{200B}", "\\u{200B}") + .replace("\u{ad}", "\\u{AD}") + .replace("\u{2060}", "\\u{2060}"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9c5a12ea9c8..03f8c5a2c07 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -164,6 +164,8 @@ define_Conf! { (max_fn_params_bools, "max_fn_params_bools": u64, 3), /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), + /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses + (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), } impl Default for Conf { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 96d9905027b..dfe2aadffc0 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -18,9 +18,9 @@ pub mod internal_lints; pub mod numeric_literal; pub mod paths; pub mod ptr; +pub mod qualify_min_const_fn; pub mod sugg; pub mod usage; -pub mod qualify_min_const_fn; pub use self::attrs::*; pub use self::diagnostics::*; @@ -47,7 +47,6 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; -use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -884,19 +883,11 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty() - } - if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, - // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { - const_eval::is_const_fn(cx.tcx, def_id) - }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 5e8800d38eb..52d3c2c1daf 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -36,8 +36,9 @@ pub struct NumericLiteral<'a> { pub integer: &'a str, /// The fraction part of the number. pub fraction: Option<&'a str>, - /// The character used as exponent separator (b'e' or b'E') and the exponent part. - pub exponent: Option<(char, &'a str)>, + /// The exponent separator (b'e' or b'E') including preceding underscore if present + /// and the exponent part. + pub exponent: Option<(&'a str, &'a str)>, /// The type suffix, including preceding underscore if present. pub suffix: Option<&'a str>, @@ -100,7 +101,7 @@ impl<'a> NumericLiteral<'a> { self.radix == Radix::Decimal } - pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) { + pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) { let mut integer = digits; let mut fraction = None; let mut exponent = None; @@ -113,12 +114,14 @@ impl<'a> NumericLiteral<'a> { fraction = Some(&digits[i + 1..]); }, 'e' | 'E' => { - if integer.len() > i { - integer = &digits[..i]; + let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i }; + + if integer.len() > exp_start { + integer = &digits[..exp_start]; } else { - fraction = Some(&digits[integer.len() + 1..i]); + fraction = Some(&digits[integer.len() + 1..exp_start]); }; - exponent = Some((c, &digits[i + 1..])); + exponent = Some((&digits[exp_start..=i], &digits[i + 1..])); break; }, _ => {}, @@ -153,7 +156,7 @@ impl<'a> NumericLiteral<'a> { } if let Some((separator, exponent)) = self.exponent { - output.push(separator); + output.push_str(separator); Self::group_digits(&mut output, exponent, group_size, true, false); } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index be837a61dc0..277da9d3f3a 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -41,6 +41,9 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; +pub const FN: [&str; 3] = ["core", "ops", "Fn"]; +pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; +pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; diff --git a/clippy_lints/src/utils/qualify_min_const_fn.rs b/clippy_lints/src/utils/qualify_min_const_fn.rs index 19d890b4554..1b4f2034272 100644 --- a/clippy_lints/src/utils/qualify_min_const_fn.rs +++ b/clippy_lints/src/utils/qualify_min_const_fn.rs @@ -1,9 +1,12 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::mir::*; +use rustc_middle::mir::{ + Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, +}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; -use rustc_span::symbol::{sym}; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi::RustIntrinsic; use std::borrow::Cow; @@ -24,15 +27,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { | ty::PredicateAtom::ConstEvaluatable(..) | ty::PredicateAtom::ConstEquate(..) | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue, - ty::PredicateAtom::ObjectSafe(_) => { - panic!("object safe predicate on function: {:#?}", predicate) - } - ty::PredicateAtom::ClosureKind(..) => { - panic!("closure kind predicate on function: {:#?}", predicate) - } - ty::PredicateAtom::Subtype(_) => { - panic!("subtype predicate on function: {:#?}", predicate) - } + ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), + ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), + ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), ty::PredicateAtom::Trait(pred, _) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; @@ -48,12 +45,12 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { on const fn parameters are unstable" .into(), )); - } + }, // other kinds of bounds are either tautologies // or cause errors in other passes _ => continue, } - } + }, } } match predicates.parent { @@ -93,24 +90,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { match ty.kind() { 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())); + }, ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), ty::FnPtr(..) => { - return Err((span, "function pointers in const fn are unstable".into())); - } + return Err((span, "function pointers in const fn are unstable".into())); + }, ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { - ty::ExistentialPredicate::AutoTrait(_) - | ty::ExistentialPredicate::Projection(_) => { + ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => { return Err(( span, "trait bounds other than `Sized` \ on const fn parameters are unstable" .into(), )); - } + }, ty::ExistentialPredicate::Trait(trait_ref) => { if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() { return Err(( @@ -120,34 +116,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { .into(), )); } - } + }, } } - } - _ => {} + }, + _ => {}, } } Ok(()) } -fn check_rvalue( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - rvalue: &Rvalue<'tcx>, - span: Span, -) -> McfResult { +fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult { match rvalue { - Rvalue::ThreadLocalRef(_) => { - Err((span, "cannot access thread local storage in const fn".into())) - } - Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => { - check_operand(tcx, operand, span, body) - } - Rvalue::Len(place) - | Rvalue::Discriminant(place) - | Rvalue::Ref(_, _, place) - | Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, body), + Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), + Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body), + Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { + check_place(tcx, *place, span, body) + }, Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { use rustc_middle::ty::cast::CastTy; let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); @@ -155,20 +140,16 @@ fn check_rvalue( match (cast_in, cast_out) { (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { Err((span, "casting pointers to ints is unstable in const fn".into())) - } + }, _ => check_operand(tcx, operand, span, body), } - } - Rvalue::Cast( - CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), - operand, - _, - ) => check_operand(tcx, operand, span, body), + }, + Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => { + check_operand(tcx, operand, span, body) + }, Rvalue::Cast( CastKind::Pointer( - PointerCast::UnsafeFnPointer - | PointerCast::ClosureFnPointer(_) - | PointerCast::ReifyFnPointer, + PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer, ), _, _, @@ -178,10 +159,7 @@ fn check_rvalue( deref_ty.ty } else { // We cannot allow this for now. - return Err(( - span, - "unsizing casts are only allowed for references right now".into(), - )); + return Err((span, "unsizing casts are only allowed for references right now".into())); }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { @@ -192,7 +170,7 @@ fn check_rvalue( // We just can't allow trait objects until we have figured out trait method calls. Err((span, "unsizing casts are not allowed in const fn".into())) } - } + }, // binops are fine on integers Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { check_operand(tcx, lhs, span, body)?; @@ -201,13 +179,14 @@ fn check_rvalue( if ty.is_integral() || ty.is_bool() || ty.is_char() { Ok(()) } else { - Err((span, "only int, `bool` and `char` operations are stable in const fn".into())) + Err(( + span, + "only int, `bool` and `char` operations are stable in const fn".into(), + )) } - } + }, Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()), - Rvalue::NullaryOp(NullOp::Box, _) => { - Err((span, "heap allocations are not allowed in const fn".into())) - } + Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); if ty.is_integral() || ty.is_bool() { @@ -215,39 +194,29 @@ fn check_rvalue( } else { Err((span, "only int and `bool` operations are stable in const fn".into())) } - } + }, Rvalue::Aggregate(_, operands) => { for operand in operands { check_operand(tcx, operand, span, body)?; } Ok(()) - } + }, } } -fn check_statement( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - statement: &Statement<'tcx>, -) -> McfResult { +fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult { let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { - check_place(tcx, *place, span, body)?; + check_place(tcx, *place, span, body)?; check_rvalue(tcx, body, def_id, rval, span) - } - - StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, body), + }, + StatementKind::FakeRead(_, place) | // just an assignment - StatementKind::SetDiscriminant { place, .. } => { - check_place(tcx, **place, span, body) - } + StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body), - StatementKind::LlvmInlineAsm { .. } => { - Err((span, "cannot use inline assembly in const fn".into())) - } + StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), // These are all NOPs StatementKind::StorageLive(_) @@ -259,12 +228,7 @@ fn check_statement( } } -fn check_operand( - tcx: TyCtxt<'tcx>, - operand: &Operand<'tcx>, - span: Span, - body: &Body<'tcx>, -) -> McfResult { +fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { match operand { Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body), Operand::Constant(c) => match c.check_static_ptr(tcx) { @@ -274,14 +238,9 @@ fn check_operand( } } -fn check_place( - tcx: TyCtxt<'tcx>, - place: Place<'tcx>, - span: Span, - body: &Body<'tcx>, -) -> McfResult { +fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { + while let [ref proj_base @ .., elem] = *cursor { cursor = proj_base; match elem { ProjectionElem::Field(..) => { @@ -289,26 +248,22 @@ fn check_place( if let Some(def) = base_ty.ty_adt_def() { // No union field accesses in `const fn` if def.is_union() { - return Err((span, "accessing union fields is unstable".into())); + return Err((span, "accessing union fields is unstable".into())); } } - } + }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Deref - | ProjectionElem::Index(_) => {} + | ProjectionElem::Index(_) => {}, } } Ok(()) } -fn check_terminator( - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - terminator: &Terminator<'tcx>, -) -> McfResult { +fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { TerminatorKind::FalseEdge { .. } @@ -318,20 +273,23 @@ fn check_terminator( | TerminatorKind::Resume | TerminatorKind::Unreachable => Ok(()), - TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body), + TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body), TerminatorKind::DropAndReplace { place, value, .. } => { - check_place(tcx, *place, span, body)?; + check_place(tcx, *place, span, body)?; check_operand(tcx, value, span, body) - } + }, - TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => { - check_operand(tcx, discr, span, body) - } + TerminatorKind::SwitchInt { + discr, + switch_ty: _, + values: _, + targets: _, + } => check_operand(tcx, discr, span, body), TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { Err((span, "const fn generators are unstable".into())) - } + }, TerminatorKind::Call { func, @@ -343,8 +301,7 @@ fn check_terminator( } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { - if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) - { + if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) { return Err(( span, format!( @@ -360,9 +317,7 @@ fn check_terminator( // within const fns. `transmute` is allowed in all other const contexts. // This won't really scale to more intrinsics or functions. Let's allow const // transmutes in const fn before we add more hacks to this. - if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic - && tcx.item_name(fn_def_id) == sym::transmute - { + if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute { return Err(( span, "can only call `transmute` from const items, not `const fn`".into(), @@ -378,14 +333,16 @@ fn check_terminator( } else { Err((span, "can only call other const fns within const fn".into())) } - } + }, - TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => { - check_operand(tcx, cond, span, body) - } + TerminatorKind::Assert { + cond, + expected: _, + msg: _, + target: _, + cleanup: _, + } => 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())), } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index fac63bcb993..d9d60fffcd7 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -235,8 +235,19 @@ impl EarlyLintPass for Write { } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { + fn is_build_script(cx: &EarlyContext<'_>) -> bool { + // Cargo sets the crate name for build scripts to `build_script_build` + cx.sess + .opts + .crate_name + .as_ref() + .map_or(false, |crate_name| crate_name == "build_script_build") + } + if mac.path == sym!(println) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + if !is_build_script(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { span_lint_and_sugg( @@ -251,7 +262,9 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + if !is_build_script(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( diff --git a/clippy_workspace_tests/build.rs b/clippy_workspace_tests/build.rs new file mode 100644 index 00000000000..3507168a3a9 --- /dev/null +++ b/clippy_workspace_tests/build.rs @@ -0,0 +1,7 @@ +#![deny(clippy::print_stdout)] + +fn main() { + // Test for #6041 + println!("Hello"); + print!("Hello"); +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index f6d529de9a3..0dba5a71c50 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -110,7 +110,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "borrow_interior_mutable_const", - group: "correctness", + group: "style", desc: "referencing `const` with interior mutability", deprecation: None, module: "non_copy_const", @@ -334,7 +334,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "declare_interior_mutable_const", - group: "correctness", + group: "style", desc: "declaring `const` with interior mutability", deprecation: None, module: "non_copy_const", @@ -381,6 +381,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "derive", }, + Lint { + name: "disallowed_method", + group: "nursery", + desc: "use of a disallowed method call", + deprecation: None, + module: "disallowed_method", + }, Lint { name: "diverging_sub_expression", group: "complexity", @@ -885,6 +892,20 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "attrs", }, + Lint { + name: "inline_asm_x86_att_syntax", + group: "restriction", + desc: "prefer Intel x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, + Lint { + name: "inline_asm_x86_intel_syntax", + group: "restriction", + desc: "prefer AT&T x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, Lint { name: "inline_fn_without_body", group: "correctness", @@ -941,6 +962,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "invisible_characters", + group: "correctness", + desc: "using an invisible character in a string literal, which is confusing", + deprecation: None, + module: "unicode", + }, Lint { name: "items_after_statements", group: "pedantic", @@ -2782,13 +2810,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, - Lint { - name: "zero_width_space", - group: "correctness", - desc: "using a zero-width space in a string literal, which is confusing", - deprecation: None, - module: "unicode", - }, Lint { name: "zst_offset", group: "correctness", diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 697823712bf..f0d73e9b0e2 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -71,7 +71,7 @@ fn default_config() -> compiletest::Config { } config.target_rustcflags = Some(format!( - "-L {0} -L {1} -Dwarnings -Zui-testing {2}", + "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}", host_lib().join("deps").display(), cargo::TARGET_LIB.join("deps").display(), third_party_crates(), diff --git a/tests/ui-toml/toml_disallowed_method/clippy.toml b/tests/ui-toml/toml_disallowed_method/clippy.toml new file mode 100644 index 00000000000..a1f515e443d --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/clippy.toml @@ -0,0 +1 @@ +disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match"] diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs new file mode 100644 index 00000000000..3d3f0729abd --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs @@ -0,0 +1,13 @@ +#![warn(clippy::disallowed_method)] + +extern crate regex; +use regex::Regex; + +fn main() { + let a = vec![1, 2, 3, 4]; + let re = Regex::new(r"ab.*c").unwrap(); + + re.is_match("abc"); + + a.iter().sum::(); +} diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr new file mode 100644 index 00000000000..ed91b5a6796 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr @@ -0,0 +1,16 @@ +error: use of a disallowed method `regex::re_unicode::Regex::is_match` + --> $DIR/conf_disallowed_method.rs:10:5 + | +LL | re.is_match("abc"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-method` implied by `-D warnings` + +error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum` + --> $DIR/conf_disallowed_method.rs:12:5 + | +LL | a.iter().sum::(); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6fbba01416a..103ec27e7d7 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/asm_syntax.rs b/tests/ui/asm_syntax.rs new file mode 100644 index 00000000000..658cae397e1 --- /dev/null +++ b/tests/ui/asm_syntax.rs @@ -0,0 +1,31 @@ +#![feature(asm)] +// only-x86_64 + +#[warn(clippy::inline_asm_x86_intel_syntax)] +mod warn_intel { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +#[warn(clippy::inline_asm_x86_att_syntax)] +mod warn_att { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +fn main() { + unsafe { + warn_att::use_asm(); + warn_intel::use_asm(); + } +} diff --git a/tests/ui/asm_syntax.stderr b/tests/ui/asm_syntax.stderr new file mode 100644 index 00000000000..27b51166eac --- /dev/null +++ b/tests/ui/asm_syntax.stderr @@ -0,0 +1,44 @@ +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:7:9 + | +LL | asm!(""); + | ^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:8:9 + | +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:9:9 + | +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:21:9 + | +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` + = help: use Intel x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:22:9 + | +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: aborting due to 5 previous errors + diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index e6626d57a77..de670cdfc31 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -1,7 +1,8 @@ +// compile-flags: --emit=link // no-prefer-dynamic #![crate_type = "proc-macro"] -#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] +#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] #![allow(clippy::useless_conversion)] extern crate proc_macro; @@ -11,7 +12,11 @@ extern crate syn; use proc_macro::TokenStream; use quote::{quote, quote_spanned}; use syn::parse_macro_input; -use syn::{parse_quote, ItemTrait, TraitItem}; +use syn::spanned::Spanned; +use syn::token::Star; +use syn::{ + parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type, +}; #[proc_macro_attribute] pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { @@ -35,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { } TokenStream::from(quote!(#item)) } + +#[proc_macro_attribute] +pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream { + fn make_name(count: usize) -> String { + format!("'life{}", count) + } + + fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> { + let arg = sig.inputs.first_mut()?; + if let FnArg::Typed(PatType { pat, .. }) = arg { + if let Pat::Ident(PatIdent { ident, .. }) = &**pat { + if ident == "self" { + return Some(arg); + } + } + } + None + } + + let mut elided = 0; + let mut item = parse_macro_input!(input as ItemImpl); + + // Look for methods having arbitrary self type taken by &mut ref + for inner in &mut item.items { + if let ImplItem::Method(method) = inner { + if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) { + if let box Type::Reference(reference) = &mut pat_type.ty { + // Target only unnamed lifetimes + let name = match &reference.lifetime { + Some(lt) if lt.ident == "_" => make_name(elided), + None => make_name(elided), + _ => continue, + }; + elided += 1; + + // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. + // In order to avoid adding the dependency, get a default span from a non-existent token. + // A default span is needed to mark the code as coming from expansion. + let span = Star::default().span(); + + // Replace old lifetime with the named one + let lifetime = Lifetime::new(&name, span); + reference.lifetime = Some(parse_quote!(#lifetime)); + + // Add lifetime to the generics of the method + method.sig.generics.params.push(parse_quote!(#lifetime)); + } + } + } + } + + TokenStream::from(quote!(#item)) +} diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 05ffb55f620..3df8be6c232 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic #![crate_type = "proc-macro"] diff --git a/tests/ui/crashes/associated-constant-ice.rs b/tests/ui/crashes/associated-constant-ice.rs index 4bb833795bb..948deba3ea6 100644 --- a/tests/ui/crashes/associated-constant-ice.rs +++ b/tests/ui/crashes/associated-constant-ice.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/1698 pub trait Trait { diff --git a/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/tests/ui/crashes/auxiliary/proc_macro_crash.rs index 086548e58ed..619d11cefc4 100644 --- a/tests/ui/crashes/auxiliary/proc_macro_crash.rs +++ b/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic // ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro // crates. If we don't set this, compiletest will override the `crate_type` attribute below and diff --git a/tests/ui/crashes/cc_seme.rs b/tests/ui/crashes/cc_seme.rs index c48c7e9e6c6..98588be9cf8 100644 --- a/tests/ui/crashes/cc_seme.rs +++ b/tests/ui/crashes/cc_seme.rs @@ -1,5 +1,3 @@ -// run-pass - #[allow(dead_code)] /// Test for https://github.com/rust-lang/rust-clippy/issues/478 diff --git a/tests/ui/crashes/enum-glob-import-crate.rs b/tests/ui/crashes/enum-glob-import-crate.rs index db1fa871afe..dca32aa3b56 100644 --- a/tests/ui/crashes/enum-glob-import-crate.rs +++ b/tests/ui/crashes/enum-glob-import-crate.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] #![allow(unused_imports)] diff --git a/tests/ui/crashes/ice-1588.rs b/tests/ui/crashes/ice-1588.rs index 15d0f705b36..b0a3d11bce4 100644 --- a/tests/ui/crashes/ice-1588.rs +++ b/tests/ui/crashes/ice-1588.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/tests/ui/crashes/ice-1782.rs b/tests/ui/crashes/ice-1782.rs index 1ca6b6976b3..81af88962a6 100644 --- a/tests/ui/crashes/ice-1782.rs +++ b/tests/ui/crashes/ice-1782.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, unused_variables)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/tests/ui/crashes/ice-1969.rs b/tests/ui/crashes/ice-1969.rs index 837ec9df31a..96a8fe6c24d 100644 --- a/tests/ui/crashes/ice-1969.rs +++ b/tests/ui/crashes/ice-1969.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1969 diff --git a/tests/ui/crashes/ice-2499.rs b/tests/ui/crashes/ice-2499.rs index ffef1631775..45b3b1869dd 100644 --- a/tests/ui/crashes/ice-2499.rs +++ b/tests/ui/crashes/ice-2499.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)] /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` diff --git a/tests/ui/crashes/ice-2594.rs b/tests/ui/crashes/ice-2594.rs index ac19f1976e9..3f3986b6fc6 100644 --- a/tests/ui/crashes/ice-2594.rs +++ b/tests/ui/crashes/ice-2594.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, unused_variables)] /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` diff --git a/tests/ui/crashes/ice-2727.rs b/tests/ui/crashes/ice-2727.rs index d832c286033..56024abc8f5 100644 --- a/tests/ui/crashes/ice-2727.rs +++ b/tests/ui/crashes/ice-2727.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2727 pub fn f(new: fn()) { diff --git a/tests/ui/crashes/ice-2760.rs b/tests/ui/crashes/ice-2760.rs index 9e5e299c336..f1a229f3f4f 100644 --- a/tests/ui/crashes/ice-2760.rs +++ b/tests/ui/crashes/ice-2760.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow( unused_variables, clippy::blacklisted_name, diff --git a/tests/ui/crashes/ice-2774.rs b/tests/ui/crashes/ice-2774.rs index 47f8e3b18ee..d44b0fae820 100644 --- a/tests/ui/crashes/ice-2774.rs +++ b/tests/ui/crashes/ice-2774.rs @@ -1,5 +1,3 @@ -// run-pass - use std::collections::HashSet; // See rust-lang/rust-clippy#2774. diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr new file mode 100644 index 00000000000..0c2d48f938f --- /dev/null +++ b/tests/ui/crashes/ice-2774.stderr @@ -0,0 +1,10 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/ice-2774.rs:15:1 + | +LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-lifetimes` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-2862.rs b/tests/ui/crashes/ice-2862.rs index 47324ce1831..8326e3663b0 100644 --- a/tests/ui/crashes/ice-2862.rs +++ b/tests/ui/crashes/ice-2862.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2862 pub trait FooMap { diff --git a/tests/ui/crashes/ice-2865.rs b/tests/ui/crashes/ice-2865.rs index c4f6c0fed68..6b1ceb50569 100644 --- a/tests/ui/crashes/ice-2865.rs +++ b/tests/ui/crashes/ice-2865.rs @@ -1,5 +1,3 @@ -// run-pass - #[allow(dead_code)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 diff --git a/tests/ui/crashes/ice-3151.rs b/tests/ui/crashes/ice-3151.rs index ffad2d06b56..fef4d7db84d 100644 --- a/tests/ui/crashes/ice-3151.rs +++ b/tests/ui/crashes/ice-3151.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 #[derive(Clone)] diff --git a/tests/ui/crashes/ice-3462.rs b/tests/ui/crashes/ice-3462.rs index 95c7dff9be3..7d62e315da2 100644 --- a/tests/ui/crashes/ice-3462.rs +++ b/tests/ui/crashes/ice-3462.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::all)] #![allow(clippy::blacklisted_name)] #![allow(unused)] diff --git a/tests/ui/crashes/ice-3741.rs b/tests/ui/crashes/ice-3741.rs index a548415da62..1253ddcfaeb 100644 --- a/tests/ui/crashes/ice-3741.rs +++ b/tests/ui/crashes/ice-3741.rs @@ -1,5 +1,4 @@ // aux-build:proc_macro_crash.rs -// run-pass #![warn(clippy::suspicious_else_formatting)] diff --git a/tests/ui/crashes/ice-3747.rs b/tests/ui/crashes/ice-3747.rs index d0b44ebafee..cdf018cbc88 100644 --- a/tests/ui/crashes/ice-3747.rs +++ b/tests/ui/crashes/ice-3747.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/3747 macro_rules! a { diff --git a/tests/ui/crashes/ice-4727.rs b/tests/ui/crashes/ice-4727.rs index cdb59caec67..2a4bc83f58a 100644 --- a/tests/ui/crashes/ice-4727.rs +++ b/tests/ui/crashes/ice-4727.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::use_self)] #[path = "auxiliary/ice-4727-aux.rs"] diff --git a/tests/ui/crashes/ice-4760.rs b/tests/ui/crashes/ice-4760.rs index ead67d5ed1b..08b06961760 100644 --- a/tests/ui/crashes/ice-4760.rs +++ b/tests/ui/crashes/ice-4760.rs @@ -1,4 +1,3 @@ -// run-pass const COUNT: usize = 2; struct Thing; trait Dummy {} diff --git a/tests/ui/crashes/ice-700.rs b/tests/ui/crashes/ice-700.rs index b06df83d51a..0cbceedbd6b 100644 --- a/tests/ui/crashes/ice-700.rs +++ b/tests/ui/crashes/ice-700.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/700 diff --git a/tests/ui/crashes/ice_exacte_size.rs b/tests/ui/crashes/ice_exacte_size.rs index e02eb28ab86..30e4b11ec0b 100644 --- a/tests/ui/crashes/ice_exacte_size.rs +++ b/tests/ui/crashes/ice_exacte_size.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1336 diff --git a/tests/ui/crashes/if_same_then_else.rs b/tests/ui/crashes/if_same_then_else.rs index 4ef992b05e7..2f913292995 100644 --- a/tests/ui/crashes/if_same_then_else.rs +++ b/tests/ui/crashes/if_same_then_else.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::comparison_chain)] #![deny(clippy::if_same_then_else)] diff --git a/tests/ui/crashes/issue-825.rs b/tests/ui/crashes/issue-825.rs index 3d4a88ab3c4..05696e3d7d5 100644 --- a/tests/ui/crashes/issue-825.rs +++ b/tests/ui/crashes/issue-825.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(warnings)] /// Test for https://github.com/rust-lang/rust-clippy/issues/825 diff --git a/tests/ui/crashes/issues_loop_mut_cond.rs b/tests/ui/crashes/issues_loop_mut_cond.rs index c4acd5cda1b..bb238c81ebc 100644 --- a/tests/ui/crashes/issues_loop_mut_cond.rs +++ b/tests/ui/crashes/issues_loop_mut_cond.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code)] /// Issue: https://github.com/rust-lang/rust-clippy/issues/2596 diff --git a/tests/ui/crashes/match_same_arms_const.rs b/tests/ui/crashes/match_same_arms_const.rs index 848f0ea52ca..94c939665e6 100644 --- a/tests/ui/crashes/match_same_arms_const.rs +++ b/tests/ui/crashes/match_same_arms_const.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::match_same_arms)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2427 diff --git a/tests/ui/crashes/mut_mut_macro.rs b/tests/ui/crashes/mut_mut_macro.rs index d8fbaa54146..a238e7896fc 100644 --- a/tests/ui/crashes/mut_mut_macro.rs +++ b/tests/ui/crashes/mut_mut_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)] #![allow(dead_code)] diff --git a/tests/ui/crashes/needless_borrow_fp.rs b/tests/ui/crashes/needless_borrow_fp.rs index 48507efe1e9..4f61c76828d 100644 --- a/tests/ui/crashes/needless_borrow_fp.rs +++ b/tests/ui/crashes/needless_borrow_fp.rs @@ -1,5 +1,3 @@ -// run-pass - #[deny(clippy::all)] #[derive(Debug)] pub enum Error { diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/tests/ui/crashes/needless_lifetimes_impl_trait.rs index bd1fa4a0b1e..676564b2445 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.rs +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::needless_lifetimes)] #![allow(dead_code)] diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr new file mode 100644 index 00000000000..d68bbe78802 --- /dev/null +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -0,0 +1,14 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes_impl_trait.rs:15:5 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/needless_lifetimes_impl_trait.rs:1:9 + | +LL | #![deny(clippy::needless_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/crashes/procedural_macro.rs b/tests/ui/crashes/procedural_macro.rs index f79d9ab6460..c7468493380 100644 --- a/tests/ui/crashes/procedural_macro.rs +++ b/tests/ui/crashes/procedural_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #[macro_use] extern crate clippy_mini_macro_test; diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index 3d5063d1a3a..a41bcb33b44 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::blacklisted_name)] pub fn foo(bar: *const u8) { diff --git a/tests/ui/crashes/returns.rs b/tests/ui/crashes/returns.rs index f2153efc388..8021ed4607d 100644 --- a/tests/ui/crashes/returns.rs +++ b/tests/ui/crashes/returns.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/1346 #[deny(warnings)] diff --git a/tests/ui/crashes/single-match-else.rs b/tests/ui/crashes/single-match-else.rs index 3a4bbe310cc..1ba7ac08213 100644 --- a/tests/ui/crashes/single-match-else.rs +++ b/tests/ui/crashes/single-match-else.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::single_match_else)] //! Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/tests/ui/crashes/trivial_bounds.rs b/tests/ui/crashes/trivial_bounds.rs index 2bb95c18a39..60105a8213f 100644 --- a/tests/ui/crashes/trivial_bounds.rs +++ b/tests/ui/crashes/trivial_bounds.rs @@ -1,5 +1,3 @@ -// run-pass - #![feature(trivial_bounds)] #![allow(unused, trivial_bounds)] diff --git a/tests/ui/crashes/used_underscore_binding_macro.rs b/tests/ui/crashes/used_underscore_binding_macro.rs index 265017c51d9..6d2124c12fe 100644 --- a/tests/ui/crashes/used_underscore_binding_macro.rs +++ b/tests/ui/crashes/used_underscore_binding_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::useless_attribute)] //issue #2910 #[macro_use] diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index aa6ef162fe4..81d8221bd13 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -38,54 +38,54 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { continue; } count += 1; - println!("{}", count); } // should not trigger the lint because the count is conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { count += 1; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; if ch == 'a' { continue; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { let _ = 123; } - println!("{}", count); } // should not trigger the lint because the count is incremented multiple times let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { count += 1; } - println!("{}", count); } } } @@ -96,30 +96,30 @@ mod issue_3308 { let mut skips = 0; let erasures = vec![]; for i in 0..10 { + println!("{}", skips); while erasures.contains(&(i + skips)) { skips += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); let mut j = 0; while j < 5 { skips += 1; j += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); for j in 0..5 { skips += 1; } - println!("{}", skips); } } } @@ -145,3 +145,16 @@ mod issue_4732 { let _closure = || println!("index: {}", index); } } + +mod issue_4677 { + pub fn test() { + let slice = &[1, 2, 3]; + + // should not trigger the lint because the count is used after incremented + let mut count = 0; + for _i in slice { + count += 1; + println!("{}", count); + } + } +} diff --git a/tests/ui/inconsistent_digit_grouping.fixed b/tests/ui/inconsistent_digit_grouping.fixed index b75f10917df..dd683e7f746 100644 --- a/tests/ui/inconsistent_digit_grouping.fixed +++ b/tests/ui/inconsistent_digit_grouping.fixed @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } diff --git a/tests/ui/inconsistent_digit_grouping.rs b/tests/ui/inconsistent_digit_grouping.rs index 79ce38be19b..d5d27c853c2 100644 --- a/tests/ui/inconsistent_digit_grouping.rs +++ b/tests/ui/inconsistent_digit_grouping.rs @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.rs b/tests/ui/needless_arbitrary_self_type_unfixable.rs new file mode 100644 index 00000000000..a39d96109f1 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.rs @@ -0,0 +1,45 @@ +// aux-build:proc_macro_attr.rs + +#![warn(clippy::needless_arbitrary_self_type)] + +#[macro_use] +extern crate proc_macro_attr; + +mod issue_6089 { + // Check that we don't lint if the `self` parameter comes from expansion + + macro_rules! test_from_expansion { + () => { + trait T1 { + fn test(self: &Self); + } + + struct S1 {} + + impl T1 for S1 { + fn test(self: &Self) {} + } + }; + } + + test_from_expansion!(); + + // If only the lifetime name comes from expansion we will lint, but the suggestion will have + // placeholders and will not be applied automatically, as we can't reliably know the original name. + // This specific case happened with async_trait. + + trait T2 { + fn call_with_mut_self(&mut self); + } + + struct S2 {} + + // The method's signature will be expanded to: + // fn call_with_mut_self<'life0>(self: &'life0 mut Self) {} + #[rename_my_lifetimes] + impl T2 for S2 { + fn call_with_mut_self(self: &mut Self) {} + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.stderr b/tests/ui/needless_arbitrary_self_type_unfixable.stderr new file mode 100644 index 00000000000..44a0e6ddeac --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.stderr @@ -0,0 +1,10 @@ +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type_unfixable.rs:41:31 + | +LL | fn call_with_mut_self(self: &mut Self) {} + | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self` + | + = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 913cd004f19..d482d466e44 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -259,4 +259,102 @@ mod issue4291 { } } +mod issue2944 { + trait Foo {} + struct Bar {} + struct Baz<'a> { + bar: &'a Bar, + } + + impl<'a> Foo for Baz<'a> {} + impl Bar { + fn baz<'a>(&'a self) -> impl Foo + 'a { + Baz { bar: self } + } + } +} + +mod nested_elision_sites { + // issue #issue2944 + + // closure trait bounds subject to nested elision + // don't lint because they refer to outer lifetimes + fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + // don't lint + fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 { + f() + } + fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + // lint + fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + f(i) + } + fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { + f(i) + } + + // don't lint + fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + // lint + fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { + f(i) + } + + // don't lint + fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + // lint + fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 + where + T: Fn(&i32) -> &i32, + { + f(i) + } + + // don't lint + fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 { + f(i) + } + fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } + // lint + fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { + f(i) + } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + |f| () + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index d3a360ed8b5..c8a2e8b81c0 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -102,5 +102,53 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:271:9 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:300:5 + | +LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:303:5 + | +LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:312:5 + | +LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:324:5 + | +LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:339:5 + | +LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:352:5 + | +LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:355:5 + | +LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors diff --git a/tests/ui/needless_range_loop2.rs b/tests/ui/needless_range_loop2.rs index a82b1159161..7633316e0f8 100644 --- a/tests/ui/needless_range_loop2.rs +++ b/tests/ui/needless_range_loop2.rs @@ -82,6 +82,20 @@ fn main() { for i in 1..3 { println!("{}", arr[i]); } + + // Fix #5945 + let mut vec = vec![1, 2, 3, 4]; + for i in 0..vec.len() - 1 { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() - 1 { + vec[i] += 1; + } } mod issue2277 { diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 5fb568672d3..2045ffdb5f0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); + let mut map = HashMap::::new(); + map.entry(42).or_insert_with(String::new); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert_with(String::new); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -110,23 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); -} - fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 737b0f7e55b..522f31b72d0 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -110,23 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); -} - fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index b8a436993f3..bc5978b538f 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -60,23 +60,35 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:62:19 + | +LL | map.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:65:21 + | +LL | btree.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:62:21 + --> $DIR/or_fun_call.rs:68:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:87:35 + --> $DIR/or_fun_call.rs:93:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:91:10 + --> $DIR/or_fun_call.rs:97:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/print_stdout_build_script.rs b/tests/ui/print_stdout_build_script.rs new file mode 100644 index 00000000000..997ebef8a69 --- /dev/null +++ b/tests/ui/print_stdout_build_script.rs @@ -0,0 +1,12 @@ +// compile-flags: --crate-name=build_script_build + +#![warn(clippy::print_stdout)] + +fn main() { + // Fix #6041 + // + // The `print_stdout` lint shouldn't emit in `build.rs` + // as these methods are used for the build script. + println!("Hello"); + print!("Hello"); +} diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs index b4a931043b0..ac4c1bc6597 100644 --- a/tests/ui/temporary_assignment.rs +++ b/tests/ui/temporary_assignment.rs @@ -1,5 +1,4 @@ #![warn(clippy::temporary_assignment)] -#![allow(const_item_mutation)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr index 4cc32c79f05..7d79901a28d 100644 --- a/tests/ui/temporary_assignment.stderr +++ b/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> $DIR/temporary_assignment.rs:48:5 + --> $DIR/temporary_assignment.rs:47:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1; = note: `-D clippy::temporary-assignment` implied by `-D warnings` error: assignment to temporary - --> $DIR/temporary_assignment.rs:49:5 + --> $DIR/temporary_assignment.rs:48:5 | LL | / MultiStruct { LL | | structure: Struct { field: 0 }, @@ -17,13 +17,13 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:54:5 + --> $DIR/temporary_assignment.rs:53:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:55:5 + --> $DIR/temporary_assignment.rs:54:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^ diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index 27db9594f3b..1f596c312fe 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -1,7 +1,11 @@ -#[warn(clippy::zero_width_space)] +#[warn(clippy::invisible_characters)] fn zero() { print!("Here >​< is a ZWS, and ​another"); print!("This\u{200B}is\u{200B}fine"); + print!("Here >­< is a SHY, and ­another"); + print!("This\u{ad}is\u{ad}fine"); + print!("Here >⁠< is a WJ, and ⁠another"); + print!("This\u{2060}is\u{2060}fine"); } #[warn(clippy::unicode_not_nfc)] diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 4575a132e5b..3fca463c620 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,13 +1,25 @@ -error: zero-width space detected +error: invisible character detected --> $DIR/unicode.rs:3:12 | LL | print!("Here >​< is a ZWS, and ​another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` | - = note: `-D clippy::zero-width-space` implied by `-D warnings` + = note: `-D clippy::invisible-characters` implied by `-D warnings` + +error: invisible character detected + --> $DIR/unicode.rs:5:12 + | +LL | print!("Here >­< is a SHY, and ­another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` + +error: invisible character detected + --> $DIR/unicode.rs:7:12 + | +LL | print!("Here >⁠< is a WJ, and ⁠another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"` error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:9:12 + --> $DIR/unicode.rs:13:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -15,12 +27,12 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:15:12 + --> $DIR/unicode.rs:19:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` | = note: `-D clippy::non-ascii-literal` implied by `-D warnings` -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors diff --git a/util/dev b/util/dev deleted file mode 100755 index 319de217e0d..00000000000 --- a/util/dev +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -CARGO_TARGET_DIR=$(pwd)/target/ -export CARGO_TARGET_DIR - -echo 'Deprecated! `util/dev` usage is deprecated, please use `cargo dev` instead.' - -cd clippy_dev && cargo run -- "$@"