Skip to content
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

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

Open
pnkfelix opened this issue Sep 6, 2016 · 26 comments
Open
Labels
A-macros Macro related proposals and issues A-macros-libstd Proposals that introduce new standard library macros T-lang Relevant to the language team, which will review and decide on the RFC. T-libs-api Relevant to the library API team, which will review and decide on the RFC.

Comments

@pnkfelix
Copy link
Member

pnkfelix commented Sep 6, 2016

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
Copy link
Contributor

oli-obk commented Sep 6, 2016

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

@nrc nrc added T-lang Relevant to the language team, which will review and decide on the RFC. T-libs-api Relevant to the library API team, which will review and decide on the RFC. labels Sep 8, 2016
@NeoLegends
Copy link

NeoLegends commented Sep 20, 2016

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
Copy link

mqudsi commented Nov 19, 2017

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
Copy link
Member

da-x commented Jan 21, 2018

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
Copy link

ghost commented Jan 29, 2018

@da-x quick question: #1719 (comment)

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

    test
    main
    main::inner
    main::inner::inner_a
    main::inner2
    main::inner2::inner_b

function_name!() would return:

    test
    main
    inner
    inner_a
    inner2
    inner_b

The following example( playground ) gives an idea why function_path!() could be just as useless as function_name!(), line!() however saves the day:

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
Copy link
Member

da-x commented Jan 29, 2018

@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
Copy link

LEXUGE commented Feb 12, 2018

So, What's the status of it?

@da-x
Copy link
Member

da-x commented Apr 6, 2018

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
Copy link
Member

da-x commented Apr 9, 2018

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
Copy link
Member

joshtriplett commented Apr 12, 2018

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

@joshtriplett
Copy link
Member

joshtriplett commented Apr 12, 2018

We discussed this (and rust-lang/rust#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:

  • Bikeshedding the name ;)
  • Not breaking if people have a macro named function! already; may need better macro module handling (Tracking issue for "macro naming and modularisation" (RFC #1561) rust#35896). For instance, we need a test to make sure that a macro using this function! doesn't break if expanded in a file that defines its own macro named function!.
  • What happens if you use this in a closure? Should it identify the closure somehow, or the containing named function, or both?

@polarathene
Copy link

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
Copy link

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
Copy link
Contributor

Centril commented Aug 15, 2018

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
Copy link

prasannavl commented Oct 17, 2018

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

Eg:
#466
#1719
rust-lang/rust#35651
rust-lang/rust#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
Copy link
Member

da-x commented Jan 21, 2019

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
Copy link

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

@joshtriplett
Copy link
Member

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.

@Centril Centril added the A-macros Macro related proposals and issues label Nov 13, 2019
@Centril Centril added the A-macros-libstd Proposals that introduce new standard library macros label Nov 13, 2019
@Lokathor
Copy link
Contributor

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
Copy link
Member

@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
Copy link
Member

eddyb commented Nov 13, 2019

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
Copy link
Contributor

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
Copy link
Member

(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
Copy link
Member

@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
Copy link
Contributor

uh, sure

@Lokathor
Copy link
Contributor

Proper PR opened #2818

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-macros Macro related proposals and issues A-macros-libstd Proposals that introduce new standard library macros T-lang Relevant to the language team, which will review and decide on the RFC. T-libs-api Relevant to the library API team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests