Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions clippy_lints/src/operators/arithmetic_side_effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,18 @@ impl ArithmeticSideEffects {

/// Checks if the lhs and the rhs types of a binary operation like "addition" or
/// "multiplication" are present in the inner set of allowed types.
fn has_allowed_binary(&self, lhs_ty: Ty<'_>, rhs_ty: Ty<'_>) -> bool {
fn has_allowed_binary(&self, cx: &LateContext<'_>, lhs_ty: Ty<'_>, rhs_ty: Ty<'_>) -> bool {
if let (ty::Float(lhs_float), ty::Float(rhs_float)) = (lhs_ty.kind(), rhs_ty.kind())
&& lhs_float == rhs_float
&& matches!(lhs_float, ty::FloatTy::F32 | ty::FloatTy::F64)
{
return true;
}

if lhs_ty.is_diag_item(cx, sym::String) && (rhs_ty.is_str() || rhs_ty.is_diag_item(cx, sym::String)) {
return true;
}

let lhs_ty_string = lhs_ty.to_string();
let lhs_ty_string_elem = lhs_ty_string.split('<').next().unwrap_or_default();
let rhs_ty_string = rhs_ty.to_string();
Expand All @@ -86,7 +97,17 @@ impl ArithmeticSideEffects {

/// Checks if the type of an unary operation like "negation" is present in the inner set of
/// allowed types.
fn has_allowed_unary(&self, ty: Ty<'_>) -> bool {
fn has_allowed_unary(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
if let ty::Float(float_ty) = ty.kind()
&& matches!(float_ty, ty::FloatTy::F32 | ty::FloatTy::F64)
{
return true;
}

if matches!(ty.opt_diag_name(cx), Some(sym::Saturating | sym::Wrapping)) {
return true;
}

let ty_string = ty.to_string();
let ty_string_elem = ty_string.split('<').next().unwrap_or_default();
self.allowed_unary.contains(ty_string_elem)
Expand Down Expand Up @@ -167,7 +188,7 @@ impl ArithmeticSideEffects {

let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs();
let rhs_ty = cx.typeck_results().expr_ty_adjusted(actual_rhs).peel_refs();
if self.has_allowed_binary(lhs_ty, rhs_ty)
if self.has_allowed_binary(cx, lhs_ty, rhs_ty)
| has_specific_allowed_type_and_operation(cx, lhs_ty, op, rhs_ty)
| is_safe_due_to_smaller_source_type(cx, op, (actual_lhs, lhs_ty), actual_rhs)
| is_safe_due_to_smaller_source_type(cx, op, (actual_rhs, rhs_ty), actual_lhs)
Expand Down Expand Up @@ -253,7 +274,7 @@ impl ArithmeticSideEffects {
return;
}
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
if self.has_allowed_unary(ty) {
if self.has_allowed_unary(cx, ty) {
return;
}
let actual_un_expr = peel_hir_expr_refs(un_expr).0;
Expand Down
22 changes: 15 additions & 7 deletions clippy_utils/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,23 @@ pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec<DefId> {
/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1
/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2
pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec<DefId> {
let (root, rest) = match *path {
[] | [_] => return Vec::new(),
[root, ref rest @ ..] => (root, rest),
};

let mut out = Vec::new();
for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) {
lookup_with_base(tcx, base, ns, rest, &mut out);

match *path {
[] => {},
// Single segment: search from current crate root
// This allows resolving local type names like "Foo" from clippy.toml
[_name] => {
lookup_with_base(tcx, LOCAL_CRATE.as_def_id(), ns, path, &mut out);
},
// Multi-segment paths: existing behavior
[root, ref rest @ ..] => {
for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) {
lookup_with_base(tcx, base, ns, rest, &mut out);
}
},
}

out
}

Expand Down
13 changes: 13 additions & 0 deletions tests/ui/arithmetic_side_effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,19 @@ pub fn hard_coded_allowed() {
let _ = inferred_saturating + inferred_saturating;
let _ = inferred_string + "";
let _ = inferred_wrapping + inferred_wrapping;

// Type aliases should also be allowed (regression test for type alias handling)
type MySaturating = Saturating<u32>;
type MyWrapping = Wrapping<u32>;
type MyString = String;

let sat_alias: MySaturating = Saturating(0u32);
let wrap_alias: MyWrapping = Wrapping(0u32);
let str_alias: MyString = String::new();

let _ = sat_alias + sat_alias;
let _ = wrap_alias + wrap_alias;
let _ = str_alias + "";
}

#[rustfmt::skip]
Expand Down
Loading