From db7382179a6f351b85ea5b38f565f610814e6c30 Mon Sep 17 00:00:00 2001 From: Jim Carey Date: Sat, 16 May 2026 22:58:11 +0200 Subject: [PATCH] Implement interchangeable_params --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/interchangeable_params.rs | 631 +++++++++++++++++++++ clippy_lints/src/lib.rs | 2 + tests/ui/interchangeable_params.rs | 117 ++++ tests/ui/interchangeable_params.stderr | 158 ++++++ 6 files changed, 910 insertions(+) create mode 100644 clippy_lints/src/interchangeable_params.rs create mode 100644 tests/ui/interchangeable_params.rs create mode 100644 tests/ui/interchangeable_params.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 95f26ee09bbe..d850d5f89ab3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6906,6 +6906,7 @@ Released 2018-09-13 [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division [`integer_division_remainder_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division_remainder_used +[`interchangeable_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#interchangeable_params [`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref [`into_iter_without_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_without_iter diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 516f6e775e08..5add2bfaa152 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -234,6 +234,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, crate::inline_trait_bounds::INLINE_TRAIT_BOUNDS_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, + crate::interchangeable_params::INTERCHANGEABLE_PARAMS_INFO, crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO, crate::item_name_repetitions::MODULE_INCEPTION_INFO, crate::item_name_repetitions::MODULE_NAME_REPETITIONS_INFO, diff --git a/clippy_lints/src/interchangeable_params.rs b/clippy_lints/src/interchangeable_params.rs new file mode 100644 index 000000000000..3e97d9fc6eb1 --- /dev/null +++ b/clippy_lints/src/interchangeable_params.rs @@ -0,0 +1,631 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet, snippet_indent}; +use clippy_utils::str_utils::to_camel_case; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + Body, ExprKind, FnDecl, FnRetTy, GenericParamKind, HirId, Lifetime, LifetimeKind, LifetimeParamKind, + LifetimeSource, LifetimeSyntax, MissingLifetimeKind, Node, Param, Pat, PatKind, QPath, Ty, TyKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::def_id::LocalDefId; +use rustc_span::symbol::Symbol; +use rustc_span::{BytePos, Span}; +use std::backtrace::Backtrace; +use std::borrow::Borrow; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; + +declare_clippy_lint! { + /// ### What it does + /// Looks for functions that have multiple parameters of the same type + /// + /// ### Why is this bad? + /// It is easy to confuse the order of the same typed parameters, resulting + /// in possible bugs that the type system won't catch. + /// ### Example + /// ```no_run + /// fn transfer(customer: String, store: String) {} + /// ``` + /// Use instead: + /// ```no_run + /// struct Customer(String); + /// struct Store(String); + /// + /// // you can implement the Deref trait to remove the need for Store.0, etc. + /// fn transfer(customer: Customer, store: Store) {} + /// ``` + #[clippy::version = "1.97.0"] + pub INTERCHANGEABLE_PARAMS, + restriction, + "Detects functions with multiple same type parameters, and suggests changing them + into newtypes." +} + +declare_lint_pass!(InterchangeableParams => [INTERCHANGEABLE_PARAMS]); + +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] +enum ArgTypeEnum { + Path, + Ref, + Ptr, + Tup, +} + +#[derive(Clone, Debug)] +struct ArgTypeData { + argtype: ArgTypeEnum, + is_mut: bool, + tyname: Option, + lifetime: Option, + tups: Option>, + is_unsafe: bool, +} +impl PartialEq for ArgTypeData { + fn eq(&self, other: &Self) -> bool { + self.tyname == other.tyname + } +} +impl Eq for ArgTypeData {} + +impl Hash for ArgTypeData { + fn hash(&self, state: &mut H) { + self.tyname.hash(state); + } +} +#[derive(PartialEq, Debug)] +enum FnNodeType { + Item, + ImplItem, + TraitItem, + Closure, + Other, +} +#[derive(Debug)] +struct DeclString(String); + +impl From for DeclString { + fn from(raw: String) -> Self { + Self(raw) + } +} +impl From for String { + fn from(newtype: DeclString) -> String { + newtype.0 + } +} +impl Deref for DeclString { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Borrow for DeclString { + fn borrow(&self) -> &String { + &self.0 + } +} +impl fmt::Display for DeclString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Delegate formatting directly to the inner String's Display impl + write!(f, "{}", self.0) + } +} +#[derive(Debug)] +struct NewTypeString(String); +impl From for NewTypeString { + fn from(raw: String) -> Self { + Self(raw) + } +} +impl From for String { + fn from(newtype: NewTypeString) -> String { + newtype.0 + } +} +impl Deref for NewTypeString { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Borrow for NewTypeString { + fn borrow(&self) -> &String { + &self.0 + } +} +impl fmt::Display for NewTypeString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Delegate formatting directly to the inner String's Display impl + write!(f, "{}", self.0) + } +} + +impl<'tcx> LateLintPass<'tcx> for InterchangeableParams { + #[allow(clippy::too_many_lines)] + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fnkind: FnKind<'tcx>, + fndecl: &'tcx FnDecl<'tcx>, + fnbody: &'tcx Body<'tcx>, + span: Span, + localdefid: LocalDefId, + ) { + if span.from_expansion() { + return; + } + let fnnode = cx.tcx.hir_node_by_def_id(localdefid); + let fnsym = cx.tcx.item_name(localdefid.to_def_id()); + let fnname = fnsym.as_str(); + eprintln!("{fnname}"); + if fnname != "verify_affine_point_is_on_the_curve_scaled" { + return; + } + let is_impl = match fnnode { + Node::Item(..) => FnNodeType::Item, + Node::ImplItem(..) => FnNodeType::ImplItem, + Node::TraitItem(..) => FnNodeType::TraitItem, + Node::Expr(e) => { + if matches!(e.kind, ExprKind::Closure(..)) { + FnNodeType::Closure + } else { + FnNodeType::Other + } + }, + _ => FnNodeType::Other, + }; + // for now, bail on impl types and closures + if is_impl != FnNodeType::Item { + return; + } + + let mut funsafe = false; + if let FnKind::ItemFn(_, generics, fnheader) = fnkind { + if !generics.params.is_empty() { + // we have to allow elided lifetime generics + for gp in generics.params { + match &gp.kind { + GenericParamKind::Lifetime { kind: lt } => match lt { + LifetimeParamKind::Elided(MissingLifetimeKind::Ampersand) | LifetimeParamKind::Explicit => { + }, + LifetimeParamKind::Elided(_) | LifetimeParamKind::Error => return, + }, + _ => { + return; + }, + } + } + } + funsafe = fnheader.is_unsafe(); + } + + let paramcount = fndecl.inputs.len(); + let mut types: Vec = Vec::with_capacity(paramcount); + let mut names: Vec> = Vec::with_capacity(paramcount); + let mut paramhash: FxHashMap = FxHashMap::default(); + for argtype in fndecl.inputs { + let atdopt = process_argtypes(cx, argtype, &mut paramhash, funsafe); + let Some(atd) = atdopt else { + return; + }; + types.push(atd.clone()); + paramhash.entry(atd).and_modify(|counter| *counter += 1).or_insert(1); + } + for argname in fnbody.params { + names.push(*argname); + } + if names.len() < 2 { + return; // nead at least 2 parameters to handle + } + let paramspan = span.with_hi(fnbody.value.span.lo()); + + #[allow(rustc::potential_query_instability)] // only looking for values > 2, order doesn't matter + if !paramhash.clone().into_values().any(|x| x > 1) { + return; // no duplicates + } + + // at this point, we are ready to generate the lint text. Right now, + // we are only here if we have a standalone function, so we can suggest + // newtypes. + let mut newtypes: Vec = Vec::with_capacity(paramcount); + let mut decls: Vec = Vec::with_capacity(paramcount); + + // we process the parameters here. We've extracted the types into a structure + // that breaks out things like tuples. We haven't done that to the names yet, but the + // information is there. + + for i in 0..paramcount { + if types[i].argtype == ArgTypeEnum::Tup { + //tups are special. We have multiple names and types enclosed in parens + // for now, handle one level deep... + // in this case, the tups field contains a list of atd structs, iterate over them. + // also, build an array of name strings. + let thistup = ::clone(&types[i]); + let mut pnames: Vec> = Vec::new(); + let mut ptypes: Vec = Vec::new(); + dbg!(&names[i]); + if let PatKind::Tuple(subnames, _) = names[i].pat.kind { + for namepat in subnames { + pnames.push(*namepat); + } + + if let Some(ttups) = thistup.tups { + for j in 0..ttups.len() { + let tupnewopt = build_type_text( + cx, + ::clone(&ttups[j]), + pnames[j], + ¶mhash.clone(), + ); + let (decl, newtype) = tupnewopt; + newtypes.push(newtype); + ptypes.push(decl); + } + // construct the tuple declaration + let mut dnames: Vec = Vec::new(); + for name in pnames { + let symname = cx.tcx.hir_name(name.hir_id); + dnames.push(symname.to_string()); + } + let symside = dnames.join(", "); + let typeside = ptypes + .iter() + .map(DeclString::to_string) + .collect::>() + .join(", "); + let namedecls = DeclString(format!("{typeside}")); + decls.push(namedecls); + } else { + return; + } + } else if let PatKind::Binding(bindingmode, _, nident, _) = names[i].pat.kind { + let paramname = format!("{}{}", bindingmode.prefix_str(), nident.as_str()); + let mut tuptypes: Vec = Vec::new(); + if let Some(ttups) = thistup.tups { + // don't want to do type lookup here, single name param, tuple type + for j in 0..ttups.len() { + let tyname = match ttups[j].tyname.clone() { + Some(x) => x, + None => { + eprintln!("no name {}", line!()); + return; // abort + }, + }; + tuptypes.push(tyname); + } + let decl = DeclString(format!("{}", tuptypes.join(", "))); + ptypes.push(decl); + } else { + let tupnewopt = build_type_text( + cx, + ::clone(&types[i]), + pnames[i], + ¶mhash.clone(), + ); + let (typename, newtype) = tupnewopt; + ptypes.push(typename); + } + let typeside = ptypes + .iter() + .map(DeclString::to_string) + .collect::>() + .join(", "); + } else if let PatKind::Wild = names[i].pat.kind { + eprintln!("In Tuple with wild card {}", line!()) + } else { + return; + } + + let decnewopt = build_type_text( + cx, + ::clone(&types[i]), + *names[i].pat, + ¶mhash.clone(), + ); + let (decl, newtype) = decnewopt; + if !newtype.is_empty() { + newtypes.push(newtype); + } + let symside = if matches!(names[i].pat.kind, PatKind::Wild) { + "_".to_string() + } else { + let symname = cx.tcx.hir_name(names[i].pat.hir_id); + symname.as_str().to_string() + }; + let namedecls: String = format!("{symside}: {decl}"); + decls.push(DeclString(namedecls)); + } else { + let (decl, newtype) = build_type_text( + cx, + ::clone(&types[i]), + *names[i].pat, + ¶mhash.clone(), + ); + if !newtype.is_empty() { + newtypes.push(newtype); + } + let symside = if matches!(names[i].pat.kind, PatKind::Wild) { + "_".to_string() + } else { + let symname = cx.tcx.hir_name(names[i].pat.hir_id); + symname.as_str().to_string() + }; + let namedecls: String = format!("{symside}: {decl}"); + + decls.push(DeclString(namedecls)); + } + } + + // let lastspan = names.last().unwrap_or(&names[0]).span; + let fnspan = span.with_hi(names[0].span.lo() - BytePos(1)); + let indent = snippet_indent(cx, fnspan).unwrap_or_default(); + let fnstartsnip = snippet(cx, fnspan, ".."); + let indenttxt = format!("\n{indent}"); + let output = match fndecl.output { + FnRetTy::DefaultReturn(_) => String::new(), + FnRetTy::Return(ty) => { + format!("-> {}", snippet(cx, ty.span, "..")) + }, + }; + if newtypes.is_empty() { + // no newtypes created, don't signal + return; + } + let ntjoin = newtypes + .iter() + .map(|item| item.0.as_str()) + .collect::>() + .join(indenttxt.as_str()); + let dcljoin = decls + .iter() + .map(|item| item.0.as_str()) + .collect::>() + .join(", "); + let sugg = format!("{ntjoin}\n{indent}/* .. */\n{indent}{fnstartsnip}({dcljoin}) {output}"); + + span_lint_and_sugg( + cx, + INTERCHANGEABLE_PARAMS, + paramspan, + "multiple parameters with the same type may be confusing", + "consider using newtypes:", + sugg, + Applicability::HasPlaceholders, + ); + + // things to look for + // Generic types + // "standard arguments" -- x,y,z,a,b, rhs,lhs, self + // things in trait impl, possibly in impl. + // &Self + // let item = cx.tcx.item(); + } +} + +fn create_prefix(cx: &LateContext<'_>, atd: &ArgTypeData) -> String { + match atd.argtype { + ArgTypeEnum::Ref => { + let lifetime = handle_lifetime(cx, &atd.clone()); + if atd.is_mut { + format!("&{lifetime}mut ") + } else { + format!("&{lifetime}") + } + }, + ArgTypeEnum::Ptr => { + if atd.is_mut { + "*mut ".to_string() + } else if atd.is_unsafe { + "*const ".to_string() + } else { + "*".to_string() + } + }, + _ => String::new(), + } +} + +fn handle_lifetime(cx: &LateContext<'_>, atd: &ArgTypeData) -> String { + if let Some(lifetime) = atd.lifetime + && matches!(lifetime.source, LifetimeSource::Reference) + { + match lifetime.syntax { + LifetimeSyntax::Implicit => String::new(), + LifetimeSyntax::ExplicitAnonymous => "._".to_string(), + LifetimeSyntax::ExplicitBound => { + if let LifetimeKind::Param(defid) = lifetime.kind { + format!("{} ", cx.tcx.item_name(defid)) + } else { + String::new() + } + }, + } + } else { + String::new() + } +} +fn process_argtypes( + cx: &LateContext<'_>, + argtype: &Ty<'_>, + paramhash: &mut FxHashMap, + is_unsafe: bool, +) -> Option { + match argtype.kind { + TyKind::Path(qpath) => { + let nameopt = qpath_to_name(cx, qpath, argtype.hir_id); + let name = nameopt?; + let atd = ArgTypeData { + argtype: ArgTypeEnum::Path, + is_mut: false, + tyname: Some(name.to_string()), + lifetime: None, + tups: None, + is_unsafe, + }; + Some(atd) + }, + TyKind::Ref(lifetime, mutty) => { + // Need to handle TyKind::Slice. + let name = match mutty.ty.kind { + TyKind::Path(qpath) => { + let snippet = snippet(cx, qpath.span(), ".."); + snippet.to_string() + }, + TyKind::Slice(ty) => { + if let TyKind::Path(qpath) = ty.kind { + let snippet = snippet(cx, qpath.span(), ".."); + snippet.to_string() + } else { + return None; + } + }, + _ => { + return None; + }, + }; + let atd = ArgTypeData { + argtype: ArgTypeEnum::Ref, + is_mut: false, + tyname: Some(name), + lifetime: Some(*lifetime), + tups: None, + is_unsafe, + }; + + Some(atd) + }, + + TyKind::Ptr(mutty) => match mutty.ty.kind { + TyKind::Path(qpath) => { + let nameopt = qpath_to_name(cx, qpath, argtype.hir_id); + let name = nameopt?; + let atd = ArgTypeData { + argtype: ArgTypeEnum::Ptr, + is_mut: mutty.mutbl.is_mut(), + tyname: Some(name.to_string()), + lifetime: None, + tups: None, + is_unsafe, + }; + Some(atd) + }, + TyKind::Tup(tupdata) => { + let mut tuptypes: Vec = Vec::new(); + for thisty in tupdata { + let adtopt = process_argtypes(cx, thisty, &mut *paramhash, is_unsafe); + let adt = adtopt?; + tuptypes.push(adt.clone()); + } + let atd = ArgTypeData { + argtype: ArgTypeEnum::Ptr, + is_mut: mutty.mutbl.is_mut(), + tyname: None, + lifetime: None, + tups: Some(tuptypes), + is_unsafe, + }; + Some(atd) + }, + + _ => None, + }, + TyKind::Tup(tupdata) => { + // tuples are a sort of recursive thing. tupdata is an array + // of types within the tuple. We should walk the type tree to add the types to the + // param hash. + let mut tuptypes: Vec = Vec::new(); + for thisty in tupdata { + let adtopt = process_argtypes(cx, thisty, &mut *paramhash, is_unsafe); + let atd = adtopt?; + tuptypes.push(atd.clone()); + (*paramhash).entry(atd).and_modify(|counter| *counter += 1).or_insert(1); + } + let atd = ArgTypeData { + argtype: ArgTypeEnum::Tup, + is_mut: false, + tyname: None, + lifetime: None, + tups: Some(tuptypes), + is_unsafe, + }; + Some(atd) + }, + _ => None, + } +} + +fn qpath_to_name(cx: &LateContext<'_>, qpath: QPath<'_>, hir_id: HirId) -> Option { + let res = cx.qpath_res(&qpath, hir_id); + match res { + Res::PrimTy(ptype) => Some(ptype.name()), + Res::SelfTyAlias { alias_to: defid, .. } | Res::Def(_, defid) | Res::SelfTyParam { trait_: defid, .. } => { + Some(cx.tcx.item_name(defid)) + }, + _ => None, + } +} + +fn build_type_text( + cx: &LateContext<'_>, + typeatd: ArgTypeData, + name: Pat<'_>, + paramhash: &FxHashMap, +) -> (DeclString, NewTypeString) { + let ptypename = match typeatd.tyname { + Some(ref tyname) => tyname.clone(), + None => String::new(), + }; + + // We should fiter for "standard names" + let pnameopt = if let PatKind::Binding(bindingmode, _, ident, ..) = name.kind { + Some(format!("{}{}", bindingmode.prefix_str(), ident.as_str())) + } else { + None + }; + + let camelname = if let Some(pname) = pnameopt { + to_camel_case(pname.clone().as_str()) + } else { + String::new() + }; + let (decl, newtype) = if let Some(x) = paramhash.get(&typeatd) + && *x > 1 + && !ptypename.is_empty() + && camelname != ptypename + && camelname != "" + { + // duplicate type, camel_case name not matching type + let prefix = create_prefix(cx, &typeatd.clone()); + // let suffix = create_suffix(cx, types[i]); + ( + DeclString(format!("{prefix}{camelname}")), + NewTypeString(format!("struct {camelname}({ptypename});")), + ) + } else { + let prefix = create_prefix(cx, &typeatd.clone()); + let mut ttups: Vec = Vec::new(); + let tupdata = match typeatd.tups { + Some(x) => { + for tup in x { + let tuptypename = match tup.tyname { + Some(ref tyname) => tyname.clone(), + None => String::new(), + }; + ttups.push(tuptypename); + } + format!("({})", ttups.join(", ")) + }, + None => String::new(), + }; + let decltext = format!("{prefix}{ptypename}{tupdata}"); + dbg!(&decltext); + + (DeclString(decltext.clone()), NewTypeString(String::new())) + }; + (decl, newtype) +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7b190c255888..f8b4cd9082fa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -173,6 +173,7 @@ mod init_numbered_fields; mod inline_fn_without_body; mod inline_trait_bounds; mod int_plus_one; +mod interchangeable_params; mod item_name_repetitions; mod items_after_statements; mod items_after_test_module; @@ -860,6 +861,7 @@ rustc_lint::late_lint_methods!( ManualNoopWaker: manual_noop_waker::ManualNoopWaker = manual_noop_waker::ManualNoopWaker::new(conf), ByteCharSlice: byte_char_slices::ByteCharSlice = byte_char_slices::ByteCharSlice, ManualAssertEq: manual_assert_eq::ManualAssertEq = manual_assert_eq::ManualAssertEq, + InterchangeableParams: interchangeable_params::InterchangeableParams = interchangeable_params::InterchangeableParams, // add late passes here, used by `cargo dev new_lint` ]] ); diff --git a/tests/ui/interchangeable_params.rs b/tests/ui/interchangeable_params.rs new file mode 100644 index 000000000000..1b9ea113d115 --- /dev/null +++ b/tests/ui/interchangeable_params.rs @@ -0,0 +1,117 @@ +#![warn(clippy::interchangeable_params)] +#![allow(clippy::needless_lifetimes, clippy::ptr_arg)] +use std::sync::atomic::AtomicPtr; +//@no-rustfix: suggestions reference out of scope lifetimes/types + +// Standard library type +fn transfer(from: String, to: String) { + //~^ interchangeable_params + todo!(); +} +fn samename(string: String, mystring: String) {} +//~^ interchangeable_params +fn fn0(a: String, b: i32) {} +// primitive type +fn fn2(a: u32, b: u64, c: &mut u32, d: i64) {} +//~^ interchangeable_params + +fn fn3(param1: String, param2: i64) {} + +struct LocalType(String); + +fn fn4(first: LocalType, second: LocalType, third: String) {} +//~^ interchangeable_params + +fn fn5<'a, 'b>(dogfood: &'a u32, catfood: u32, fishfood: &'b mut u32) {} +//~^ interchangeable_params +struct MyStruct { + a: u32, + b: u32, +} + +impl MyStruct { + fn mystruct1(self, first: MyStruct, second: MyStruct) {} + + fn mystruct2(self, first: MyStruct, second: &Self, third: &Self) {} + + fn mystruct3(first: MyStruct, second: u32, third: usize) {} +} + +fn has_generics(list: &[T], i: u32) -> &T { + &list[0] +} + +#[derive(Clone, Copy)] +pub struct RandomState { + pub(crate) k0: u64, + pub(crate) k1: u64, + pub(crate) k2: u64, + pub(crate) k3: u64, +} + +impl RandomState { + fn write_usize(self, a: usize) { + todo!(); + } + fn write_u64(self, a: u64) { + todo!(); + } + fn finish(self) -> u64 { + todo!(); + } + fn new() -> RandomState { + RandomState { + k0: 0, + k1: 0, + k2: 0, + k3: 0, + } + } +} + +fn from_keys(a: &[u64; 4], b: &[u64; 4], c: usize) -> RandomState { + RandomState::new() +} + +fn unpack_alu(word: usize, second_word: usize, dst: *mut usize) {} +//~^ interchangeable_params + +fn data_range( + //~^ interchangeable_params + data: &[u8], // test comment + data_address: u64, + range_address: u64, + size: u64, +) -> Option<&[u8]> { + None +} +unsafe fn shallow_clone_vec( + //~^ interchangeable_params + atom: &AtomicPtr<()>, + ptr: *const (), + buf: *mut u8, + offset: *const u8, + len: usize, +) -> usize { + 17 +} + +fn verify_affine_point_is_on_the_curve_scaled( + //~^ interchangeable_params + ops: &String, + (x, y): (&u32, &u32), + a_scaled: &u32, + b_scaled: &u32, +) -> Result<(), std::fmt::Error> { + Ok(()) +} + +fn unreachtest(_: i32, _: i64, _: i64, _: *mut u8) { + unreachable!() +} + +fn memchr2(token: (u8, u8), slice: &[u8]) -> Option { + Some(17) +} + +fn main() {} diff --git a/tests/ui/interchangeable_params.stderr b/tests/ui/interchangeable_params.stderr new file mode 100644 index 000000000000..6a9106699cbc --- /dev/null +++ b/tests/ui/interchangeable_params.stderr @@ -0,0 +1,158 @@ +error: multiple parameters with the same type may be confusing + --> tests/ui/interchangeable_params.rs:7:1 + | +LL | fn transfer(from: String, to: String) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::interchangeable-params` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::interchangeable_params)]` +help: consider using newtypes: + | +LL + struct From(String); +LL + struct To(String); +LL + /* .. */ +LL ~ fn transfer(from: From, to: To) { + | + +error: multiple parameters with the same type may be confusing + --> tests/ui/interchangeable_params.rs:11:1 + | +LL | fn samename(string: String, mystring: String) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using newtypes: + | +LL + struct Mystring(String); +LL + /* .. */ +LL ~ fn samename(string: String, mystring: Mystring) {} + | + +error: multiple parameters with the same type may be confusing + --> tests/ui/interchangeable_params.rs:15:1 + | +LL | fn fn2(a: u32, b: u64, c: &mut u32, d: i64) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using newtypes: + | +LL + struct A(u32); +LL + struct C(u32); +LL + /* .. */ +LL ~ fn fn2(a: A, b: u64, c: &C, d: i64) {} + | + +error: multiple parameters with the same type may be confusing + --> tests/ui/interchangeable_params.rs:22:1 + | +LL | fn fn4(first: LocalType, second: LocalType, third: String) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using newtypes: + | +LL + struct First(LocalType); +LL + struct Second(LocalType); +LL + /* .. */ +LL ~ fn fn4(first: First, second: Second, third: String) {} + | + +error: multiple parameters with the same type may be confusing + --> tests/ui/interchangeable_params.rs:25:1 + | +LL | fn fn5<'a, 'b>(dogfood: &'a u32, catfood: u32, fishfood: &'b mut u32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using newtypes: + | +LL + struct Dogfood(u32); +LL + struct Catfood(u32); +LL + struct Fishfood(u32); +LL + /* .. */ +LL ~ fn fn5<'a, 'b>(dogfood: &'a Dogfood, catfood: Catfood, fishfood: &'b Fishfood) {} + | + +error: multiple parameters with the same type may be confusing + --> tests/ui/interchangeable_params.rs:76:1 + | +LL | fn unpack_alu(word: usize, second_word: usize, dst: *mut usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using newtypes: + | +LL + struct Word(usize); +LL + struct SecondWord(usize); +LL + struct Dst(usize); +LL + /* .. */ +LL ~ fn unpack_alu(word: Word, second_word: SecondWord, dst: *mut Dst) {} + | + +error: multiple parameters with the same type may be confusing + --> tests/ui/interchangeable_params.rs:79:1 + | +LL | / fn data_range( +LL | | +LL | | data: &[u8], // test comment +LL | | data_address: u64, +LL | | range_address: u64, +LL | | size: u64, +LL | | ) -> Option<&[u8]> { + | |___________________^ + | +help: consider using newtypes: + | +LL + struct DataAddress(u64); +LL + struct RangeAddress(u64); +LL + struct Size(u64); +LL + /* .. */ +LL + fn data_range( +LL + +LL ~ (data: &u8, data_address: DataAddress, range_address: RangeAddress, size: Size) -> Option<&[u8]>{ + | + +error: multiple parameters with the same type may be confusing + --> tests/ui/interchangeable_params.rs:88:1 + | +LL | / unsafe fn shallow_clone_vec( +LL | | +LL | | atom: &AtomicPtr<()>, +LL | | ptr: *const (), +... | +LL | | len: usize, +LL | | ) -> usize { + | |___________^ + | +help: consider using newtypes: + | +LL + struct Buf(u8); +LL + struct Offset(u8); +LL + /* .. */ +LL + unsafe fn shallow_clone_vec( +LL + +LL ~ (atom: &AtomicPtr<()>, ptr: *const (), buf: *mut Buf, offset: *const Offset, len: usize) -> usize{ + | + +error: multiple parameters with the same type may be confusing + --> tests/ui/interchangeable_params.rs:99:1 + | +LL | / fn verify_affine_point_is_on_the_curve_scaled( +LL | | +LL | | ops: &String, +LL | | (x, y): (&u32, &u32), +LL | | a_scaled: &u32, +LL | | b_scaled: &u32, +LL | | ) -> Result<(), std::fmt::Error> { + | |_________________________________^ + | +help: consider using newtypes: + | +LL + struct X(u32); +LL + struct Y(u32); +LL + struct AScaled(u32); +LL + struct BScaled(u32); +LL + /* .. */ +LL + fn verify_affine_point_is_on_the_curve_scaled( +LL + +LL ~ (ops: &String, (x, y): (&X, &Y), a_scaled: &AScaled, b_scaled: &BScaled) -> Result<(), std::fmt::Error>{ + | + +error: aborting due to 9 previous errors +