Skip to content

Tracking issue for proc_macro::Span inspection APIs #54725

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

Open
alexcrichton opened this issue Oct 1, 2018 · 124 comments
Open

Tracking issue for proc_macro::Span inspection APIs #54725

alexcrichton opened this issue Oct 1, 2018 · 124 comments
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) A-proc-macros Area: Procedural macros B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC Libs-Tracked Libs issues that are tracked on the team's project board. S-tracking-design-concerns Status: There are blocking design concerns. T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@alexcrichton
Copy link
Member

alexcrichton commented Oct 1, 2018

This issue is intended to track a number of unstable APIs which are used to inspect the contents of a Span for information like the file name, byte position, manufacturing new spans, combining them, etc.

This issue tracks the proc_macro_span unstable feature.

Public API

Already stabilized:

impl Span {
    pub fn source_text(&self) -> Option<String>;
}

impl Group {
    pub fn span_open(&self) -> Span;
    pub fn span_close(&self) -> Span;
}

Up for stabilization:

impl Span {
    pub fn start(&self) -> Span;
    pub fn end(&self) -> Span;

    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
}

Close to stabilization, pending some bikeshedding:

impl Span {
    pub fn file(&self) -> String; // Mapped file name, for display purposes.

    pub fn local_file(&self) -> Option<PathBuf>; // Real file name as it exists on disk.
    // Open question: naming? local_file? real_file? actual_file_on_disk? unmapped_file? ..?

    pub fn byte_range(&self) -> Range<usize>;
    // Open question: byte_range() -> Range? byte_offset() -> usize?
}

Things that require more discussion:

impl Span {
    pub fn eq(&self, other: &Span) -> bool;
    pub fn join(&self, other: Span) -> Option<Span>;
    pub fn parent(&self) -> Option<Span>;
    pub fn source(&self) -> Span;
}

impl Literal {
    pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span>;
}
@alexcrichton alexcrichton added A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC labels Oct 1, 2018
alexcrichton added a commit to alexcrichton/rust that referenced this issue Oct 1, 2018
@softprops
Copy link

I wanted to shed some pain this is causing with tarpaulin.

Tarpaulin has worked amazingly well for me and my company as a replacement for kcov which historically been a pain to get accurate, reliable and correct results. At the moment tarpaulin stands as the most promising goto option for codecoverage in rust and just feels more like the only first class tooling option.

Having one of those when choosing to invest in a technology is important for many company's adoption story for checking off code quantity checkmarks. When they see that rust doesn't have a reasonable code quality story that works on stable rust, that's can result in a "pass" rather than "I'll take it". There are currently some work arounds for making this work-ish on stable but it feels very much like the story serde was in a year or so ago when you wanted to show all your friends how amazing serde was but then was embarrassed to show what it took to make work on stable because of a macro stabilization blocker.

@JakeTherston
Copy link

With procedural macros having reached a point where they're very useful on stable, I expect many users will find themselves needing access to this information. Would it be reasonable to only stabilize parts of the Span API that are not too risky? Perhaps exposing a function that optionally returns the path of the file where a macro is invoked if such a file exists?

kevinmehall added a commit to kevinmehall/rust-peg that referenced this issue Nov 3, 2018
This unstable feature (rust-lang/rust#54725) is the last thing that we
require in Nightly. Removing it will cause a significant regression in
error messages, but that can be improved after switching to parsing the
grammar as tokens rather than as a string literal.
@matklad
Copy link
Member

matklad commented Feb 1, 2019

I have a concern about exposing LineColum information. It looks like it could play badly with incremental compilation, especially in the IDE context.

My understanding is that, if one adds a blank line to the start of the file, the line_column information of the input spans to proc macro changes. That means that IDE would have to re-expand procedural macros even after insignificant white space changes.

I would feel more confident if proc-macros were strictly a pure function from the input token stream to output token stream. This can be achieved, for example, by making line-column infocation relative to the start of macro invocation (as opposed to relative to the start of the file).

I don't feel that exposing absolute position is necessary the end of the world: IDE can track if a macro actually makes use of the info, or it can supply macro with some fake line/columns. But it's hard to tell if such hacks would work well in practice, and it's hard to experiment with them for the lack of IDE which handled proc-macros incrementally....

@davidlattimore
Copy link
Contributor

If the parser allocated IDs to every AST node, then, and this is the hard part, when an edit was made to the source, the parser tried to keep those IDs the same in all the non-edited code and only allocate new IDs for new nodes, that would allow spans to be kept completely separate from the AST. Those IDs could be passed through macro expansion without causing unnecessary invalidations. If something needed a span later on, it could then go back and ask the parser for the span for that particular AST node ID. I feel like having an incremental parser is important, not because parsing is the bottleneck, but because it underpins everything else.

@matklad
Copy link
Member

matklad commented Feb 2, 2019

@davidlattimore this is fascinating, but slightly off-topic for the issue. I've created a thread on internals: https://internals.rust-lang.org/t/macros-vs-incremental-parsing/9323

@est31
Copy link
Member

est31 commented May 30, 2019

The column!() macro as well as std::panic::Location::column are returning 1-based columns while the span available from the proc-macro crate is 0-based according to its docs. Is this inconsistency intended?

@est31
Copy link
Member

est31 commented May 30, 2019

This thread has more discussion about 1-based columns: #46762 (comment)

@est31
Copy link
Member

est31 commented May 30, 2019

Another open question is how this API relates to #47389 which is about minimizing span information throughout the compiler. Should stabilization be blocked until a design for #47389 is found? Is it too late already as we have set_span functionality? @michaelwoerister what do you think?

@michaelwoerister
Copy link
Member

#47389 is mostly concerned about data types that are used later in the compilation pipeline, such as type information and MIR. Exposing things at the proc-macro level should not be too bad.

@est31
Copy link
Member

est31 commented Jun 1, 2019

But rust-analyzer might one day expand the scope of incremental compilation to the parsing stage, right?

joshka added a commit to joshka/askama that referenced this issue Dec 3, 2024
Templates can now be placed directly next to the source file that they
are defined in as a default. This relies on an unstable rust compiler
feature, which exposes the source file to proc macros. See
<rust-lang/rust#54725> for more info.

This requires the nightly compiler to run, and enabling the
proc_macro_span and procmacro2_semver_exempt cfg flags. To build / test:

```shell
RUSTFLAGS='--cfg proc_macro_span --cfg procmacro2_semver_exempt' \
  cargo +nightly build
```
Fixes: <askama-rs#877>
joshka added a commit to joshka/askama that referenced this issue Dec 3, 2024
Templates can now be placed directly next to the source file that they
are defined in as a default. This relies on an unstable rust compiler
feature, which exposes the source file to proc macros. See
<rust-lang/rust#54725> for more info.

This requires the nightly compiler to run, and enabling the
proc_macro_span and procmacro2_semver_exempt cfg flags. To build / test:

```shell
RUSTFLAGS='--cfg proc_macro_span --cfg procmacro2_semver_exempt' \
  cargo +nightly build
```
Fixes: <askama-rs#877>
@ogoffart
Copy link
Contributor

I’d like to see progress on this issue. How can we help move it forward?

Source File

My main use case is to locate file references in macros, similar to how include_str!("file.txt") resolves "file.txt" relative to its span. For Slint, this would help locate referenced images or other .slint files. Right now, resolving paths relative to the manifest is unintuitive.
Another example of a use case is here

It seems the main blocker for stabilizing path() is the need to rename it to local_path() to distinguish it from the mapped path. Should I propose an API Change Proposal (ACP) in the libs-team repo for this, or a directly make a PR?
(local_path is how it is currently named in rustc)

Line/Column Information

Many use cases have been mentioned already. The main one i have in Slint is to know if an identifier token and a punctuation token are separated by space or not. Other usecases are using it with tools like xtr and cpp_build that parse code with syn and proc_macro2 outside of macros, such as in CLI tools or build scripts. Line info would help with better diagnostics. Because even if not using the proc_macro API, the proc_macro2 crate only expose what is currently stable in proc_macro.

Regarding concerns about incremental compilation, I think this is not a real issue:

  • The Span API can track whether location methods are called, so the compiler can handle location dependencies only if needed.
  • Currently, macros already require re-runs, because they depends on extra files, environment variables, timestamps, network access, etc. So span location tracking would be one of the simpler issues to solve.

Given this, I don’t see why line/column APIs couldn’t move toward stabilization. Are there other blockers preventing this?

github-actions bot pushed a commit to tautschnig/verify-rust-std that referenced this issue Mar 11, 2025
… r=m-ou-se

Stabilize span_open() and span_close().

This proposes to stabilize `Group::span_open()` and `Group::span_close()`.

These are part of the `proc_macro_span` feature gate tracked in rust-lang#54725

Most of the features gated behind `proc_macro_span` are about source location information (file path, line and column information), expansion information (parent()), source_text(), etc. Those are not ready for stabilizaiton. However, getting the span of the `(` and `)` separately instead of only of the entire `(...)` can be very useful in proc macros, and doesn't seem blocked on anything that all the other parts of `proc_macro_span` are blocked on. So, this renames the feature gate for those two functions to `proc_macro_group_span` and stabilizes them.
@m-ou-se
Copy link
Member

m-ou-se commented Apr 11, 2025

An open question is what to do with SourceFile::is_real(). It seems to always return true. Even in cases like TokenStream::from_str, the spans are just set to the call site with a real path.

Do we need is_real? Will it be used in the future? In which cases? Do we want to use an Option or some other interface for it instead?

@m-ou-se
Copy link
Member

m-ou-se commented Apr 11, 2025

I believe the best thing we can do is to remove the SourceFile type entirely and instead have:

impl Span {
    pub fn file(&self) -> String; // Mapped file name, for display purposes.
    pub fn local_file(&self) -> Option<PathBuf>; // Real file name as it exists on disk.
}

PR: #139671

@sam0x17
Copy link

sam0x17 commented Apr 11, 2025

Yes I agree this would be preferable, people are almost certainly going to be converting it to String anyway

jhpratt added a commit to jhpratt/rust that referenced this issue Apr 13, 2025
Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file}

Simplification/redesign of the unstable proc macro span API, tracked in rust-lang#54725:

Before:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn source_file(&self) -> SourceFile;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceFile { .. }

impl !Send for SourceFile {}
impl !Sync for SourceFile {}

impl SourceFile {
    pub fn path(&self) -> PathBuf;
    pub fn is_real(&self) -> bool;
}
```

After:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn file(&self) -> String; // Mapped file name, for display purposes.
    pub fn local_file(&self) -> Option<PathBuf>; // Real file name as it exists on disk.
}
```

This resolves the last blocker for stabilizing these methods. (Stabilizing will be a separate PR with FCP.)
Zalathar added a commit to Zalathar/rust that referenced this issue Apr 15, 2025
Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file}

Simplification/redesign of the unstable proc macro span API, tracked in rust-lang#54725:

Before:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn source_file(&self) -> SourceFile;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceFile { .. }

impl !Send for SourceFile {}
impl !Sync for SourceFile {}

impl SourceFile {
    pub fn path(&self) -> PathBuf;
    pub fn is_real(&self) -> bool;
}
```

After:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn file(&self) -> String; // Mapped file name, for display purposes.
    pub fn local_file(&self) -> Option<PathBuf>; // Real file name as it exists on disk.
}
```

This resolves the last blocker for stabilizing these methods. (Stabilizing will be a separate PR with FCP.)
Zalathar added a commit to Zalathar/rust that referenced this issue Apr 15, 2025
Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file}

Simplification/redesign of the unstable proc macro span API, tracked in rust-lang#54725:

Before:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn source_file(&self) -> SourceFile;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceFile { .. }

impl !Send for SourceFile {}
impl !Sync for SourceFile {}

impl SourceFile {
    pub fn path(&self) -> PathBuf;
    pub fn is_real(&self) -> bool;
}
```

After:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn file(&self) -> String; // Mapped file name, for display purposes.
    pub fn local_file(&self) -> Option<PathBuf>; // Real file name as it exists on disk.
}
```

This resolves the last blocker for stabilizing these methods. (Stabilizing will be a separate PR with FCP.)
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Apr 15, 2025
Rollup merge of rust-lang#139671 - m-ou-se:proc-macro-span, r=dtolnay

Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file}

Simplification/redesign of the unstable proc macro span API, tracked in rust-lang#54725:

Before:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn source_file(&self) -> SourceFile;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceFile { .. }

impl !Send for SourceFile {}
impl !Sync for SourceFile {}

impl SourceFile {
    pub fn path(&self) -> PathBuf;
    pub fn is_real(&self) -> bool;
}
```

After:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn file(&self) -> String; // Mapped file name, for display purposes.
    pub fn local_file(&self) -> Option<PathBuf>; // Real file name as it exists on disk.
}
```

This resolves the last blocker for stabilizing these methods. (Stabilizing will be a separate PR with FCP.)
@m-ou-se
Copy link
Member

m-ou-se commented Apr 15, 2025

PR for stabilizing start+end+line+column +file+local_file, to be discussed by the libs-api team: #139865

@m-ou-se
Copy link
Member

m-ou-se commented Apr 15, 2025

After discussion in the libs-api meeting, I've split out the file and local_file from the stabilization PR, so we can first stablize start+end+line+column. Then we have some time to discuss local_file (and its name) and byte_range (or byte_offset).

@m-ou-se
Copy link
Member

m-ou-se commented Apr 16, 2025

Opened #139901 for the open question on byte offsets. Feel free to vote 👍 or 👎 on that PR, or leave a comment there if you have more thoughts.

@m-ou-se
Copy link
Member

m-ou-se commented Apr 16, 2025

Opened #139903 for the open question on the name of local_file. Feel free to leave a comment there (or leave a 👍 reaction if there's already comment you agree with).

github-actions bot pushed a commit to rust-lang/rustc-dev-guide that referenced this issue Apr 17, 2025
Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file}

Simplification/redesign of the unstable proc macro span API, tracked in rust-lang/rust#54725:

Before:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn source_file(&self) -> SourceFile;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceFile { .. }

impl !Send for SourceFile {}
impl !Sync for SourceFile {}

impl SourceFile {
    pub fn path(&self) -> PathBuf;
    pub fn is_real(&self) -> bool;
}
```

After:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn file(&self) -> String; // Mapped file name, for display purposes.
    pub fn local_file(&self) -> Option<PathBuf>; // Real file name as it exists on disk.
}
```

This resolves the last blocker for stabilizing these methods. (Stabilizing will be a separate PR with FCP.)
github-actions bot pushed a commit to rust-lang/miri that referenced this issue Apr 17, 2025
Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file}

Simplification/redesign of the unstable proc macro span API, tracked in rust-lang/rust#54725:

Before:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn source_file(&self) -> SourceFile;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceFile { .. }

impl !Send for SourceFile {}
impl !Sync for SourceFile {}

impl SourceFile {
    pub fn path(&self) -> PathBuf;
    pub fn is_real(&self) -> bool;
}
```

After:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn file(&self) -> String; // Mapped file name, for display purposes.
    pub fn local_file(&self) -> Option<PathBuf>; // Real file name as it exists on disk.
}
```

This resolves the last blocker for stabilizing these methods. (Stabilizing will be a separate PR with FCP.)
github-actions bot pushed a commit to model-checking/verify-rust-std that referenced this issue Apr 19, 2025
Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file}

Simplification/redesign of the unstable proc macro span API, tracked in rust-lang#54725:

Before:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn source_file(&self) -> SourceFile;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceFile { .. }

impl !Send for SourceFile {}
impl !Sync for SourceFile {}

impl SourceFile {
    pub fn path(&self) -> PathBuf;
    pub fn is_real(&self) -> bool;
}
```

After:

```rust
impl Span {
    pub fn line(&self) -> usize;
    pub fn column(&self) -> usize;
    pub fn file(&self) -> String; // Mapped file name, for display purposes.
    pub fn local_file(&self) -> Option<PathBuf>; // Real file name as it exists on disk.
}
```

This resolves the last blocker for stabilizing these methods. (Stabilizing will be a separate PR with FCP.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) A-proc-macros Area: Procedural macros B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC Libs-Tracked Libs issues that are tracked on the team's project board. S-tracking-design-concerns Status: There are blocking design concerns. T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests