-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
added a lint against function item references #76269
Conversation
Please add a test for this in |
compiler/rustc_lint/src/builtin.rs
Outdated
if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, referent) = e.kind { | ||
if let hir::ExprKind::Path(qpath) = &referent.kind { | ||
if let Some(def_id) = cx.qpath_res(qpath, referent.hir_id).opt_def_id() { | ||
cx.tcx.hir().get_if_local(def_id).map(|node| { |
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.
Hmm, why is this only looking in the current crate? I'd think we'd want to link on &std::env::var
or something too.
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.
Good point, checking references to functions in other crates is probably important. I'm not sure I understand how to check other crates though. I've been looking through the docs for hir::map::Map
and ty::TyCtxt
, but if someone could give me a few pointers or help out that'd be great.
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 don't think this needs to work on the HIR at all. Try type_of
instead. type_of
returns a Ty
, so you could call matches!(ty.kind, TyKind::FnDef(_, _))
: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
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.
Yes, I think @jyn514 is right -- going through HIR here is unexpected.
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.
Actually, this is a design decision... do we want the lint to fire on
let fn_item = function_name;
let _val = &fn_item;
If yes, this should be type-based. If no, we have to instead check if the referent is the name of a function.
Shouldn't we suggest casting to a proper function pointer type rather than |
Sometimes function pointer type is really long. |
We could always suggest using underscores to prevent the types getting too long e.g. |
The lint breaks the following tests, so does it make sense to just add
Also |
That code looks like this: fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
tcx.calculate_dtor(def_id, &mut dropck::check_drop_impl)
} and |
16922, 20847, 22933, 25757 all seem deliberate... and they make me wonder if we need to improve the lint. They are all of the form So given that we have at least 5 false positives in the rustc codebase and test suite, we cannot leave the lint as-is, we have to find something smarter. The key thing that happens in all these cases is that the @oli-obk @Manishearth summoning some clippy people that can hopefully help with suggestions for how to implement such a lint. :) Basically we want to detect |
We could change the lint to a MIR lint that triggers on a |
We don't really have MIR lints as a fixed notion though, do we? We have a few ad-hoc constructions that plug into different parts of the MIR pipeline, see #72515. |
The way clippy does this is to have a normal lint that loads the mir of any function definition it sees and then analyzes that. Having a good point in the optimization pipeline is something @rust-lang/wg-mir-opt should come up with. Until we have made an informed decision, just putting it next to rust/compiler/rustc_mir/src/transform/mod.rs Line 386 in f76eda3
|
Shouldn't we rather add it to one of the three places where we already have a MIR lint? E.g. rust/compiler/rustc_mir/src/transform/mod.rs Line 309 in f76eda3
|
Oh there they are :D I thought we had that and didn't find it and then thought I imagined it. Yea, that's a good place to get deterministic MIR |
Not sure what you mean by "deterministic".^^ |
Optimizations can change things in unexpected ways, we don't want to optimize out some code on the current platform and then only lint on other platforms. So we need to do it before the main optimization pipeline |
It's still very much a WIP, but I changed lint to work with MIR. It now excludes
but I think it makes more sense to exclude it in this case. I also think we should exclude it when a function reference appears alone on the rhs of a regular let binding like I don't quite understand how to work with generics/
It's also why almost all of these tests are failing.
I also had another minor doubt. This implementation compares the spans of function references with function calls/casts/coercions to decide what function references to exclude. This works, but feels kinda janky... is there a better way to check if two MIR rvalues refer to the same thing in the source? |
f8598a4
to
7206988
Compare
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 also think we should exclude it when a function reference appears alone on the rhs of a regular let binding like
let zst_ref = &function_name
. This broke one or two existing tests wherezst_ref
was just used by casting/coercion later on. It'd be nice to get a second opinion on this though.
Good point. So, since we're only trying to avoid casts from references to fn defintions to raw pointers or ints, we may be able to simplify the logic down to just looking at Rvalue::Cast
and nothing else. Not sure if that works, let me know what you think:
- get the type of the operand in https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.Rvalue.html#variant.Cast
- if that type is
TyKind::Ref
and that inner type isTyKind::FnDef
, then we have a candidate - if the
CastKind
from theRvalue::Cast
isPointerCast::Unsize
, then we don't lint, in all other cases we lint
Is this missing some cases that we should lint or linting in cases that we should not lint?
I guess it is reasonable to start conservative and lint less; we can crank it up later if we feel we should.
Will this lint on |
☔ The latest upstream changes (presumably #78324) made this pull request unmergeable. Please resolve the merge conflicts. Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:
|
Sorry, I did not see that force push. Please leave a message when you force push |
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.
Just a few small things, then this is ready to go.
My bad, I didn't realize you wouldn't get a notification. So the lint message could be something like "taking a reference to a function item does not give a function pointer" and the suggestion could be "cast |
yes. that looks right. |
31734a0
to
c791c64
Compare
Just made the changes @oli-obk requested. I left the suggestion's applicability |
this lint suggests casting function references to `*const ()`
Working with MIR let's us exclude expressions like `&fn_name as &dyn Something` and `(&fn_name)()`. Also added ABI, unsafety and whether a function is variadic in the lint suggestion, included the `&` in the span of the lint and updated the test.
The lint checks arguments in calls to `transmute` or functions that have `Pointer` as a trait bound and displays a warning if the argument is a function reference. Also checks for `std::fmt::Pointer::fmt` to handle formatting macros although it doesn't depend on the exact expansion of the macro or formatting internals. `std::fmt::Pointer` and `std::fmt::Pointer::fmt` were also added as diagnostic items and symbols.
Removed test for the unhandled case of calls to `fn f<T>(x: &T)` where `x` is a function reference and is formatted as a pointer in `f`. This compiles since `&T` implements `Pointer`, but is unlikely to occur in practice. Also tweaked the lint's wording and modified tests accordingly.
When a function argument bound by `Pointer` is an associated type, we only perform substitutions using the parameters from the callsite but don't attempt to normalize since it may not succeed. A simplified version of the scenario that triggered this error was added as a test case. Also fixed `Pointer::fmt` which was being double-counted when called outside of macros and added a test case for this.
Added documentation for `function_item_references` lint to the rustc book and fixed comments in the lint checker itself.
… message Also updated tests accordingly and tweaked some wording in the lint declaration.
@bors r+ Thanks! |
📌 Commit c791c64 has been approved by |
☀️ Test successful - checks-actions |
this lint suggests casting function references to
*const ()
closes #75239
r? @RalfJung