Uh oh!
There was an error while loading. Please reload this page.
added a lint against function item references - #76269
Conversation
jyn514
commented
Sep 3, 2020
Please add a test for this in |
There was a problem hiding this comment.
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.
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.
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.
Yes, I think @jyn514 is right -- going through HIR here is unexpected.
There was a problem hiding this comment.
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.
ollie27
commented
Sep 3, 2020
Shouldn't we suggest casting to a proper function pointer type rather than |
tesuji
commented
Sep 3, 2020
Sometimes function pointer type is really long. |
ollie27
commented
Sep 3, 2020
We could always suggest using underscores to prevent the types getting too long e.g. |
ayrtonm
commented
Sep 4, 2020
The lint breaks the following tests, so does it make sense to just add Also |
Uh oh!
There was an error while loading. Please reload this page.
jyn514
commented
Sep 4, 2020
That code looks like this: fnadt_destructor(tcx:TyCtxt<'_>,def_id:DefId) -> Option<ty::Destructor>{
tcx.calculate_dtor(def_id,&mut dropck::check_drop_impl)}and |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
RalfJung
commented
Sep 5, 2020
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 |
oli-obk
commented
Sep 7, 2020
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. |
oli-obk
commented
Sep 7, 2020
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 |
RalfJung
commented
Sep 7, 2020
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 |
oli-obk
commented
Sep 7, 2020
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 |
RalfJung
commented
Sep 7, 2020
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? |
Uh oh!
There was an error while loading. Please reload this page.
f8598a4 to
7206988Compare
oli-obk
left a comment
There was a problem hiding this comment.
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_refwas 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::Refand that inner type isTyKind::FnDef, then we have a candidate - if the
CastKindfrom theRvalue::CastisPointerCast::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?
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
RalfJung
commented
Sep 11, 2020
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 |
Uh oh!
There was an error while loading. Please reload this page.
bors
commented
Oct 26, 2020
☔ 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: |
oli-obk
commented
Oct 26, 2020
Sorry, I did not see that force push. Please leave a message when you force push |
oli-obk
left a comment
There was a problem hiding this comment.
Just a few small things, then this is ready to go.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ayrtonm
commented
Oct 27, 2020
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 |
oli-obk
commented
Oct 27, 2020
yes. that looks right. |
31734a0 to
c791c64Compareayrtonm
commented
Oct 27, 2020
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.
oli-obk
commented
Oct 27, 2020
@bors r+ Thanks! |
bors
commented
Oct 27, 2020
📌 Commit c791c64 has been approved by |
bors
commented
Oct 27, 2020
bors
commented
Oct 27, 2020
☀️ Test successful - checks-actions |
this lint suggests casting function references to
*const ()closes#75239
r? @RalfJung