-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add a function to check whether binary oprands are nontrivial #11907
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
It's hard to check whether oprator is overrided through context of lint So assume nontrivial has overrided binary operator
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @xFrednet (or someone else) soon. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
|
Ahhh, I forget to add the case of the issue to test... |
Yes, every bug and code should be tested. You can just push a new commit with the tests 👍 |
clippy_lints/src/no_effect.rs
Outdated
}, | ||
_ => false, | ||
}; | ||
false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this actually work? It looks like this always returns false
in any case. The match
ends in a semicolon so its value looks to be effectively ignored. The false
here should probably go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I have fixed this.
clippy_lints/src/no_effect.rs
Outdated
match res { | ||
Res::Def(defkind, _) => match defkind { | ||
// user-defined | ||
DefKind::Struct | DefKind::Ctor(_, _) => true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about references to structs? Or function calls that return a struct? Or slices of structs? It might be easier to just get the type of the expression and check if it's a scalar value (char, bool, float, int).
Or does this introduce too many false negatives?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you're right. Thanks!
When I fixing this issue, I have the same question. But I'm not familiar with these utils functions clearly.
I'll fix this in next commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @y21 :)
I use method is_method_call
in rust/compiler/rustc_middle/src/ty/typeck_results.rs
for ExprKind::Binary
to check whether operator is overrided or not. It's quit simple.
As you say, if excluding scalar value of operands may introduce too many false negatives, in fact.
So I think maybe only check Struct
Enum
Union
is better? Not many people override operator with other structure, and only few of them have side effects?
I don't know what you think about it. Wait your reply!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I wasn't aware of that is_method_call
method, that looks useful here :)
Sounds like a good idea to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it helps me reduce a lot of work to check operator override !
I have pushed my code just now. You can review it.
Check whether operator is overrided with a `struct` operand. The struct here refers to `struct`, `enum`, `union`. Add and fix test for `no_effect` lint.
clippy_lints/src/no_effect.rs
Outdated
let ud_lhs = match ty_lhs { | ||
ty::Adt(adt_def, _) => adt_def.is_struct() || adt_def.is_enum() || adt_def.is_union(), | ||
_ => false, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small nit but: an ADT is always either a struct, enum or union, so the condition in the arm always returns true ^^
This could just be let ud_lhs = matches!(ty_lhs, ty::Adt(..));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, I forgot this, I'll fix it.
clippy_lints/src/no_effect.rs
Outdated
|
||
// reference: rust/compiler/rustc_middle/src/ty/typeck_results.rs: `is_method_call`. | ||
// use this function to check whether operator is overrided in `ExprKind::Binary`. | ||
(ud_lhs || ud_rhs) && tyck_result.is_method_call(expr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the is_method_call
already implied by ud_lhs || ud_rhs
? If either the LHS or RHS is an adt, then that must mean that it's a custom operator impl, right?
Actually thinking about it, can't we use just tyck_result.is_method_call(expr)
, without checking if lhs and rhs is an ADT? I think that does the same but is simpler.
is_method_call
returns false for operators on scalar types like integers, so it would still lint correctly for things like int + int;
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absoulutely! Fixed in next commit!
Simpfy code of `is_operator_overrided`, directly use `is_method_call` to check if operator is overrided, at least one oprand of binary-expr must be ADT-type So no need to check type of lhs and rhs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic of checking for a call to a custom operator impl looks good to me! I have one more suggestion (and the typo), hopefully @xFrednet doesn't mind 😅
clippy_lints/src/no_effect.rs
Outdated
fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | ||
if expr.span.from_expansion() { | ||
return false; | ||
fn is_operator_overrided(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn is_operator_overrided(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | |
fn is_operator_overriden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added check for unary operator and fix my typo, test for unary operator is added to ui/test/no_effect.*
also.
It's nice of you to give a lot reliable suggestions.
Ready to be merged !
Fix typo and add test for unary-opeator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Three small nits, but the rest looks good to me.
Also big thanks to @y21 for the review ❤️
clippy_lints/src/no_effect.rs
Outdated
fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | ||
if expr.span.from_expansion() { | ||
return false; | ||
fn is_operator_overriden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT:
fn is_operator_overriden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | |
fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { |
clippy_lints/src/no_effect.rs
Outdated
// assume nontrivial oprand of `Binary` Expr can skip `check_unnecessary_operation` | ||
if is_operator_overriden(cx, expr) { | ||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT(1): I find it a bit weird that this line also influences unnecessary_operation
but I have no idea how to rewrite it better. I think this adjustment will make it a bit clearer and then it should be fine:
// assume nontrivial oprand of `Binary` Expr can skip `check_unnecessary_operation` | |
if is_operator_overriden(cx, expr) { | |
return true; | |
} | |
if is_operator_overridden(cx, expr) { | |
// Return `true`, to prevent `check_unnecessary_operation` from | |
// linting on this statement as well. | |
return true; | |
} |
NIT(2): Could you also add tests to the file for unnecessary_operation
to ensure that it doesn't lint on this expression?
Thanks for you two too! |
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
fixes #issue11885
It's hard to check whether operator is overrided through context of lint.
So, assume non-trivial structure like tuple, array or sturt, using a overrided binary operator in this lint, which might cause a side effict.
This is not detected before.
Althrough this might weaken the ability of this lint, it may more useful than before. Maybe this lint will cause an error, but now, it not. And assuming side effect of non-trivial structure with operator is not a bad thing, right?
changelog: Fix: [
no_effect
] check if binary operands are nontrivial