rust-lang / rfcs

RFCs for changes to Rust
https://rust-lang.github.io/rfcs/
Apache License 2.0
5.93k stars 1.57k forks source link

Macro that expands into string holding function name (or module path, etc) #1743

Open pnkfelix opened 8 years ago

pnkfelix commented 8 years ago

RFC #466 was closed back in the days before we had a protocol for opening up follow-up issues to use to gather links to related discussion.

The idea is that we already have some macros (file! and line!) that are useful for reporting source code context, and it might be nice to expand that collection with macros that include other information, such as the current function name, which I believe was the suggestion of the function! macro that was at one point described in RFC #466 (whose draft contents have now been lost, apparently).

Here is my attempt to retrieve the Motivation from that original draft text. (I don't think we need the detailed design in the description of an issue like this.)

Motivation

For error reporting cases we already have file!, line!, col! and module_path! but there is no way yet to figure out in which function an error was reported. Python's tracebacks are well received and for many people the function name information is much more important than the line number.

  • An alternative (or complement) would be to have a module_path! macro that expands into a string representing the path to the current module; such a macro would be usable outside of fn definitions, e.g. in static items.
oli-obk commented 8 years ago

there's an open RFC for the function name: #1719

NeoLegends commented 8 years ago

C# has a nice implementation of this: They have the nameof()-operator, which returns the name of the type, method or variable you put into it. https://msdn.microsoft.com/de-de/library/dn986596.aspx

mqudsi commented 6 years ago

Not really. C#'s nameof is "dumb" and returns literally whatever you type into it, only as a string. With generics, nameof(T) returns T and with a function, you'd have to type the function name each time (i.e. log!("Entering {}", nameof(this_function_name)), whereas with a proper __function__ macro, that can be turned into a copy-and-paste friendly log!("Entering {}", fn_name!()) or similar.

da-x commented 6 years ago

Hi all,

I've implemented this feature over current Rust beta in:

https://github.com/da-x/rust/tree/function-macro

I have not included type parameters in the output string because it's not very useful for me. But I did include the nesting, though (a::b..).

This is my first time hacking on Rust's compiler, so bear with me that it may be ugly/inefficient, etc.

ghost commented 6 years ago

@da-x quick question: https://github.com/rust-lang/rfcs/pull/1719#issuecomment-245224914

EDIT: sounds like line numbers would be a good way to differentiate two same-named inner functions that are at the same nest-level.

EDIT2: I've decided to not post the following as a new post, and just discard it; but on second thought, I figured I might as well include it here(hidden by a `details` tag, ie. click me to open) - it's a reply attempt to the next comment by da-x > anyone needing uniqueness should tuple the result of function!() with line!() and friends. It's worth noting that such use of `line!()` would return the line number of the call, not the line number of the function definition, which is fine; but then maybe `function!()` should also not try to act like a `function_path!()`, perhaps?(although, you could argue, that the reason it does this is because for most use cases it's still a good differentiator and somewhat unlikely to hit cases like in my example below) Taking example from your [implementation](), `function_path!()` would return ```rust test main main::inner main::inner::inner_a main::inner2 main::inner2::inner_b ``` `function_name!()` would return: ```rust test main inner inner_a inner2 inner_b ``` The following example( [playground](https://play.rust-lang.org/?gist=ec28379043a4732a4bf903bad0603adf&version=stable) ) gives an idea why `function_path!()` could be just as useless as `function_name!()`, `line!()` however saves the day: ```rust fn main() { { fn inner() { println!("inner/{}", line!()); fn inner_a() { println!("inner::inner_a/{}", line!()); } inner_a(); } inner(); } fn inner() { println!("inner/{}", line!()); fn inner_a() { println!("inner::inner_a/{}", line!()); } inner_a(); } inner(); } //output: inner/4 inner::inner_a/6 inner/13 inner::inner_a/15 ```
da-x commented 6 years ago

@xftroxgpx another case to consider are two calls to function!() within in the exact same function - therefore I think that no assumption of uniqueness should be made on the result of multiple function!() invocations within a crate. So my opinion is that anyone needing uniqueness should tuple the result of function!() with line!() and friends.

LEXUGE commented 6 years ago

So, What's the status of it?

da-x commented 6 years ago

Now that I am actually using my implementation in the logging system I am writing, I fixed some bugs there. Updated my branch (also rebased ontop 1.25.0). Before, the names from methods functions, default implementation of trait methods, and the name of the struct for which the method is implemented, were all missing.

da-x commented 6 years ago

Hi all,

Although the RFC is not fully formed, I submitted an implementation as a pull request so we can have a working reference implementation that may assist in promoting the discussion and maybe reach some conclusions how to move this forward.

joshtriplett commented 6 years ago

Nominating for discussion; this has been around for a while, and it seems completely reasonable. I'd like to see this myself.

joshtriplett commented 6 years ago

We discussed this (and https://github.com/rust-lang/rust/pull/49820) in the @rust-lang/lang meeting. There was full consensus that Rust should have a macro to return the function name. Procedurally, this needs to have an RFC (or revive the old one), and an implementation with tests and a feature gate. Other open questions include:

polarathene commented 6 years ago

Not breaking if people have a macro named function!

Isn't that something that can happen with new Rust versions? The release notes could communicate it right? As long as they build their existing project with a version of Rust prior to the release with the new macro they should be fine? I guess it's more of an issue with using crates? Is crates.io able to inspect each crates src for such usage in some way?

What happens if you use this in a closure? Should it identify the closure somehow, or the containing named function, or both?

Presumably I get the file name and line that the closure was declared in, like a source map. Since the closure has no name(is using the function parameter name useful?) it might make sense to use the named function it was declared in(not file and function name that is taking the closure as a parameter, eg .map()). I think you can also define a variable to place a closure in too. my_function():closure perhaps?

lazyuser commented 6 years ago

This is quite a pain for anything related to logging and error reporting. Any chance of this making it into 2018 edition? A nightly? Anything?

  • Bikeshedding the name ;)
  • Not breaking if people have a macro named function! already

Call the built-in one __super_secret_macro_never_use_it__ and publish unofficial function-name crate defining function! which wraps the built-in. You get the point. This is something what can be done right now.

  • What happens if you use this in a closure? Should it identify the closure somehow, or the containing named function, or both?

First, you need not guarantee the stability of this macro output. Second, you can make it compatible with a backtrace/unwinding output, i.e. output whatever panic! would output. Compiler has all the corner cases implemented already. Give the user "hello_world::main::{{closure}}" and let him decide how to split/trim it.

Centril commented 6 years ago

Any chance of this making it into 2018 edition?

Unfortunately, not. It would take too much time to do all the steps required to ship this on stable by the time the edition goes into feature freeze.

prasannavl commented 6 years ago

Unfortunately, tracking this for some one just doing a search seems to be rather difficult having so many issues related to this:

Eg: https://github.com/rust-lang/rfcs/pull/466 https://github.com/rust-lang/rfcs/pull/1719 https://github.com/rust-lang/rust/issues/35651 https://github.com/rust-lang/rust/pull/49820

(.. and probably quite a few more, that left me jumping back and forth)

If this is indeed the latest issue, I think it would be quite useful to have a section in the main post, for older references and make sure it's indicated that this is the latest one, and RFC links if any. It's rather funny, for some relatively simple, it's leaving behind so much "discussion cruft".

da-x commented 5 years ago

Following the ongoing effort in Rust 2018 to make macros first class citizens like other items, perhaps it is worth to consider having function! be provided from a path, i.e. std::macros::function, so that the actual name would matter less? That way, slog and other logging crates could refer to std::macros::function directly without worrying if a user already defined function! locally.

crusty-dave commented 5 years ago

Any traction on this? I am getting tired of adding let fn_name = "func" everywhere...

joshtriplett commented 4 years ago

I'd like to see this revived as well.

@pnkfelix Do you have time to write up an RFC? It shouldn't be an especially complex RFC, and with the current state of macro scoping I think it's reasonable to add a new macro and expect that it won't break people who have their own macros.

I think if you pick a reasonable name we can hopefully avoid bikeshedding it.

The only remaining bikeshedding I know of: how should it interact with closures? I would argue for expanding to the name of the lexically containing named function, as closures don't have any meaningful name.

Lokathor commented 4 years ago

A closure's name should (bikeshed) probably be the name of the creating function with a line number or other identifier stuck on to the end, my_func#234.

I definitely have functions that end up holding more than one closure.

joshtriplett commented 4 years ago

@Lokathor Interesting. I would expect most uses of this to go along with usage of the file and line (and possibly column) macros. How would you feel about just my_func::(closure) if that assumption holds true?

eddyb commented 4 years ago

Small data point: rustc-demangle uses my_func::{closure#0} for the #2603 mangling format.

Other parts of rustc, including the legacy mangling format, haven't switched to that yet, AFAIK, but maybe they should? (it's a detail I meant to change and I've forgotten about since, TBH)

Lokathor commented 4 years ago

I wouldn't normally think to pair func_name!() with line!() and file!() because usually when i want it I want "emergency println debugging" and i'm just throwing in a few printers and i delete them once i've sorted out what's wrong.

For example, in an emulator i've been working on the past bit i've regularly wanted to just kinda keep a mild log of the control flow when adding a new part into the mix, so something like

fn draw_to_bitmap(&self, bitmap: &mut Bitmap) {

will get a temporary println!("draw_to_bitmap"); put at the top of the function, and then once i'm sure things are fine I just remove it entirely.

Another thing that I often wanted this sort of thing for is a way to format the function's name into an error message, "func: message" and similar.

That said, i have used file! and line! before, and i'm not opposed to having them help disambiguate things as well, i just wouldn't normally think to use them right away.

If my_func::{closure#0} is already somewhere in the compiler, we should just adopt that if it's easy to do so. That's readable enough.

(I assume that the particular format of this macro would be "debug string info, subject to change in the future", and that we're not going to be tied to the format we pick here forever.)

joshtriplett commented 4 years ago

(I assume that the particular format of this macro would be "debug string info, subject to change in the future", and that we're not going to be tied to the format we pick here forever.)

Agreed.

Alright, let's assume for now that closures use the same internal format that they use elsewhere, and we can always change that later.

joshtriplett commented 4 years ago

@pnkfelix @Lokathor Does one of you have time to turn this into an RFC? It doesn't need much, just an explanation of the use case (debugging) and the behavior (function name as a static string, best-effort for closures).

Lokathor commented 4 years ago

uh, sure

Lokathor commented 4 years ago

Proper PR opened https://github.com/rust-lang/rfcs/pull/2818