about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-04-15 15:47:27 +1000
committerGitHub <noreply@github.com>2025-04-15 15:47:27 +1000
commitbc4e7ad24869df27279d442294a8ec90f75d6a77 (patch)
treebfd6c4abe3c98af08e5f3f6ef72fb303036e5842
parent13cd5256acf1fdae1721047a4beb8aeeae360480 (diff)
parent1f3199c89995ee7ae5cbdaa81d1df5138660d61a (diff)
downloadrust-bc4e7ad24869df27279d442294a8ec90f75d6a77.tar.gz
rust-bc4e7ad24869df27279d442294a8ec90f75d6a77.zip
Rollup merge of #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 https://github.com/rust-lang/rust/issues/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.)
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs51
-rw-r--r--compiler/rustc_fluent_macro/src/fluent.rs3
-rw-r--r--library/proc_macro/src/bridge/client.rs6
-rw-r--r--library/proc_macro/src/bridge/mod.rs11
-rw-r--r--library/proc_macro/src/bridge/server.rs1
-rw-r--r--library/proc_macro/src/lib.rs77
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs29
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs22
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs2
-rw-r--r--tests/ui/proc-macro/auxiliary/expand-expr.rs8
-rw-r--r--tests/ui/proc-macro/auxiliary/macro-only-syntax.rs2
-rw-r--r--tests/ui/proc-macro/auxiliary/span-api-tests.rs15
-rw-r--r--tests/ui/proc-macro/span-api-tests.rs14
13 files changed, 76 insertions, 165 deletions
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 2706a5eb3d7..4edaf68c89a 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -1,5 +1,4 @@
 use std::ops::{Bound, Range};
-use std::sync::Arc;
 
 use ast::token::IdentIsRaw;
 use pm::bridge::{
@@ -18,7 +17,7 @@ use rustc_parse::parser::Parser;
 use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal};
 use rustc_session::parse::ParseSess;
 use rustc_span::def_id::CrateNum;
-use rustc_span::{BytePos, FileName, Pos, SourceFile, Span, Symbol, sym};
+use rustc_span::{BytePos, FileName, Pos, Span, Symbol, sym};
 use smallvec::{SmallVec, smallvec};
 
 use crate::base::ExtCtxt;
@@ -458,7 +457,6 @@ impl<'a, 'b> Rustc<'a, 'b> {
 impl server::Types for Rustc<'_, '_> {
     type FreeFunctions = FreeFunctions;
     type TokenStream = TokenStream;
-    type SourceFile = Arc<SourceFile>;
     type Span = Span;
     type Symbol = Symbol;
 }
@@ -664,28 +662,6 @@ impl server::TokenStream for Rustc<'_, '_> {
     }
 }
 
-impl server::SourceFile for Rustc<'_, '_> {
-    fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool {
-        Arc::ptr_eq(file1, file2)
-    }
-
-    fn path(&mut self, file: &Self::SourceFile) -> String {
-        match &file.name {
-            FileName::Real(name) => name
-                .local_path()
-                .expect("attempting to get a file path in an imported file in `proc_macro::SourceFile::path`")
-                .to_str()
-                .expect("non-UTF8 file path in `proc_macro::SourceFile::path`")
-                .to_string(),
-            _ => file.name.prefer_local().to_string(),
-        }
-    }
-
-    fn is_real(&mut self, file: &Self::SourceFile) -> bool {
-        file.is_real_file()
-    }
-}
-
 impl server::Span for Rustc<'_, '_> {
     fn debug(&mut self, span: Self::Span) -> String {
         if self.ecx.ecfg.span_debug {
@@ -695,8 +671,29 @@ impl server::Span for Rustc<'_, '_> {
         }
     }
 
-    fn source_file(&mut self, span: Self::Span) -> Self::SourceFile {
-        self.psess().source_map().lookup_char_pos(span.lo()).file
+    fn file(&mut self, span: Self::Span) -> String {
+        self.psess()
+            .source_map()
+            .lookup_char_pos(span.lo())
+            .file
+            .name
+            .prefer_remapped_unconditionaly()
+            .to_string()
+    }
+
+    fn local_file(&mut self, span: Self::Span) -> Option<String> {
+        self.psess()
+            .source_map()
+            .lookup_char_pos(span.lo())
+            .file
+            .name
+            .clone()
+            .into_local_path()
+            .map(|p| {
+                p.to_str()
+                    .expect("non-UTF8 file path in `proc_macro::SourceFile::path`")
+                    .to_string()
+            })
     }
 
     fn parent(&mut self, span: Self::Span) -> Option<Self::Span> {
diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs
index b04fd1b48f7..c96bb48a036 100644
--- a/compiler/rustc_fluent_macro/src/fluent.rs
+++ b/compiler/rustc_fluent_macro/src/fluent.rs
@@ -25,7 +25,10 @@ fn invocation_relative_path_to_absolute(span: Span, path: &str) -> PathBuf {
         path.to_path_buf()
     } else {
         // `/a/b/c/foo/bar.rs` contains the current macro invocation
+        #[cfg(bootstrap)]
         let mut source_file_path = span.source_file().path();
+        #[cfg(not(bootstrap))]
+        let mut source_file_path = span.local_file().unwrap();
         // `/a/b/c/foo/`
         source_file_path.pop();
         // `/a/b/c/foo/../locales/en-US/example.ftl`
diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs
index f6d4825c67b..e7d547966a5 100644
--- a/library/proc_macro/src/bridge/client.rs
+++ b/library/proc_macro/src/bridge/client.rs
@@ -111,12 +111,6 @@ impl Clone for TokenStream {
     }
 }
 
-impl Clone for SourceFile {
-    fn clone(&self) -> Self {
-        self.clone()
-    }
-}
-
 impl Span {
     pub(crate) fn def_site() -> Span {
         Bridge::with(|bridge| bridge.globals.def_site)
diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs
index 1b5c221425e..75d82d74654 100644
--- a/library/proc_macro/src/bridge/mod.rs
+++ b/library/proc_macro/src/bridge/mod.rs
@@ -81,16 +81,8 @@ macro_rules! with_api {
                     $self: $S::TokenStream
                 ) -> Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>;
             },
-            SourceFile {
-                fn drop($self: $S::SourceFile);
-                fn clone($self: &$S::SourceFile) -> $S::SourceFile;
-                fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool;
-                fn path($self: &$S::SourceFile) -> String;
-                fn is_real($self: &$S::SourceFile) -> bool;
-            },
             Span {
                 fn debug($self: $S::Span) -> String;
-                fn source_file($self: $S::Span) -> $S::SourceFile;
                 fn parent($self: $S::Span) -> Option<$S::Span>;
                 fn source($self: $S::Span) -> $S::Span;
                 fn byte_range($self: $S::Span) -> Range<usize>;
@@ -98,6 +90,8 @@ macro_rules! with_api {
                 fn end($self: $S::Span) -> $S::Span;
                 fn line($self: $S::Span) -> usize;
                 fn column($self: $S::Span) -> usize;
+                fn file($self: $S::Span) -> String;
+                fn local_file($self: $S::Span) -> Option<String>;
                 fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>;
                 fn subspan($self: $S::Span, start: Bound<usize>, end: Bound<usize>) -> Option<$S::Span>;
                 fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span;
@@ -120,7 +114,6 @@ macro_rules! with_api_handle_types {
             'owned:
             FreeFunctions,
             TokenStream,
-            SourceFile,
 
             'interned:
             Span,
diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs
index 97e5a603c3a..5beda7c3c96 100644
--- a/library/proc_macro/src/bridge/server.rs
+++ b/library/proc_macro/src/bridge/server.rs
@@ -82,7 +82,6 @@ with_api_handle_types!(define_server_handles);
 pub trait Types {
     type FreeFunctions: 'static;
     type TokenStream: 'static + Clone;
-    type SourceFile: 'static + Clone;
     type Span: 'static + Copy + Eq + Hash;
     type Symbol: 'static;
 }
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index f1cf0c5a2db..c46dcebedca 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -491,12 +491,6 @@ impl Span {
         Span(bridge::client::Span::mixed_site())
     }
 
-    /// The original source file into which this span points.
-    #[unstable(feature = "proc_macro_span", issue = "54725")]
-    pub fn source_file(&self) -> SourceFile {
-        SourceFile(self.0.source_file())
-    }
-
     /// The `Span` for the tokens in the previous macro expansion from which
     /// `self` was generated from, if any.
     #[unstable(feature = "proc_macro_span", issue = "54725")]
@@ -546,6 +540,25 @@ impl Span {
         self.0.column()
     }
 
+    /// The path to the source file in which this span occurs, for display purposes.
+    ///
+    /// This might not correspond to a valid file system path.
+    /// It might be remapped, or might be an artificial path such as `"<macro expansion>"`.
+    #[unstable(feature = "proc_macro_span", issue = "54725")]
+    pub fn file(&self) -> String {
+        self.0.file()
+    }
+
+    /// The path to the source file in which this span occurs on disk.
+    ///
+    /// This is the actual path on disk. It is unaffected by path remapping.
+    ///
+    /// This path should not be embedded in the output of the macro; prefer `file()` instead.
+    #[unstable(feature = "proc_macro_span", issue = "54725")]
+    pub fn local_file(&self) -> Option<PathBuf> {
+        self.0.local_file().map(|s| PathBuf::from(s))
+    }
+
     /// Creates a new span encompassing `self` and `other`.
     ///
     /// Returns `None` if `self` and `other` are from different files.
@@ -614,58 +627,6 @@ impl fmt::Debug for Span {
     }
 }
 
-/// The source file of a given `Span`.
-#[unstable(feature = "proc_macro_span", issue = "54725")]
-#[derive(Clone)]
-pub struct SourceFile(bridge::client::SourceFile);
-
-impl SourceFile {
-    /// Gets the path to this source file.
-    ///
-    /// ### Note
-    /// If the code span associated with this `SourceFile` was generated by an external macro, this
-    /// macro, this might not be an actual path on the filesystem. Use [`is_real`] to check.
-    ///
-    /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on
-    /// the command line, the path as given might not actually be valid.
-    ///
-    /// [`is_real`]: Self::is_real
-    #[unstable(feature = "proc_macro_span", issue = "54725")]
-    pub fn path(&self) -> PathBuf {
-        PathBuf::from(self.0.path())
-    }
-
-    /// Returns `true` if this source file is a real source file, and not generated by an external
-    /// macro's expansion.
-    #[unstable(feature = "proc_macro_span", issue = "54725")]
-    pub fn is_real(&self) -> bool {
-        // This is a hack until intercrate spans are implemented and we can have real source files
-        // for spans generated in external macros.
-        // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368
-        self.0.is_real()
-    }
-}
-
-#[unstable(feature = "proc_macro_span", issue = "54725")]
-impl fmt::Debug for SourceFile {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("SourceFile")
-            .field("path", &self.path())
-            .field("is_real", &self.is_real())
-            .finish()
-    }
-}
-
-#[unstable(feature = "proc_macro_span", issue = "54725")]
-impl PartialEq for SourceFile {
-    fn eq(&self, other: &Self) -> bool {
-        self.0.eq(&other.0)
-    }
-}
-
-#[unstable(feature = "proc_macro_span", issue = "54725")]
-impl Eq for SourceFile {}
-
 /// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`).
 #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
 #[derive(Clone)]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index 59293ee3f96..80f6d85a3d0 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -11,7 +11,7 @@ use std::{
 
 use intern::Symbol;
 use proc_macro::bridge::{self, server};
-use span::{FileId, Span, FIXUP_ERASED_FILE_AST_ID_MARKER};
+use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER};
 use tt::{TextRange, TextSize};
 
 use crate::server_impl::{literal_kind_to_internal, token_stream::TokenStreamBuilder, TopSubtree};
@@ -27,10 +27,6 @@ mod tt {
 
 type TokenStream = crate::server_impl::TokenStream<Span>;
 
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct SourceFile {
-    file_id: FileId,
-}
 pub struct FreeFunctions;
 
 pub struct RaSpanServer {
@@ -46,7 +42,6 @@ pub struct RaSpanServer {
 impl server::Types for RaSpanServer {
     type FreeFunctions = FreeFunctions;
     type TokenStream = TokenStream;
-    type SourceFile = SourceFile;
     type Span = Span;
     type Symbol = Symbol;
 }
@@ -245,25 +240,17 @@ impl server::TokenStream for RaSpanServer {
     }
 }
 
-impl server::SourceFile for RaSpanServer {
-    fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool {
-        file1 == file2
-    }
-    fn path(&mut self, _file: &Self::SourceFile) -> String {
-        // FIXME
-        String::new()
-    }
-    fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
-        true
-    }
-}
-
 impl server::Span for RaSpanServer {
     fn debug(&mut self, span: Self::Span) -> String {
         format!("{:?}", span)
     }
-    fn source_file(&mut self, span: Self::Span) -> Self::SourceFile {
-        SourceFile { file_id: span.anchor.file_id.file_id() }
+    fn file(&mut self, _: Self::Span) -> String {
+        // FIXME
+        String::new()
+    }
+    fn local_file(&mut self, _: Self::Span) -> Option<String> {
+        // FIXME
+        None
     }
     fn save_span(&mut self, _span: Self::Span) -> usize {
         // FIXME, quote is incompatible with third-party tools
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
index 409cf3cc781..4d7c7c46766 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -24,8 +24,6 @@ type Literal = tt::Literal;
 type Span = tt::TokenId;
 type TokenStream = crate::server_impl::TokenStream<Span>;
 
-#[derive(Clone)]
-pub struct SourceFile;
 pub struct FreeFunctions;
 
 pub struct TokenIdServer {
@@ -37,7 +35,6 @@ pub struct TokenIdServer {
 impl server::Types for TokenIdServer {
     type FreeFunctions = FreeFunctions;
     type TokenStream = TokenStream;
-    type SourceFile = SourceFile;
     type Span = Span;
     type Symbol = Symbol;
 }
@@ -223,24 +220,15 @@ impl server::TokenStream for TokenIdServer {
     }
 }
 
-impl server::SourceFile for TokenIdServer {
-    fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool {
-        true
-    }
-    fn path(&mut self, _file: &Self::SourceFile) -> String {
-        String::new()
-    }
-    fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
-        true
-    }
-}
-
 impl server::Span for TokenIdServer {
     fn debug(&mut self, span: Self::Span) -> String {
         format!("{:?}", span.0)
     }
-    fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile {
-        SourceFile {}
+    fn file(&mut self, _span: Self::Span) -> String {
+        String::new()
+    }
+    fn local_file(&mut self, _span: Self::Span) -> Option<String> {
+        None
     }
     fn save_span(&mut self, _span: Self::Span) -> usize {
         0
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
index 15de88ea656..4bd365be7ca 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
@@ -97,6 +97,7 @@ fn test_fn_like_macro_clone_raw_ident() {
 }
 
 #[test]
+#[cfg(not(bootstrap))]
 fn test_fn_like_fn_like_span_join() {
     assert_expand(
         "fn_like_span_join",
@@ -111,6 +112,7 @@ fn test_fn_like_fn_like_span_join() {
 }
 
 #[test]
+#[cfg(not(bootstrap))]
 fn test_fn_like_fn_like_span_ops() {
     assert_expand(
         "fn_like_span_ops",
diff --git a/tests/ui/proc-macro/auxiliary/expand-expr.rs b/tests/ui/proc-macro/auxiliary/expand-expr.rs
index 78c9fa75d9f..14efc3c6b9f 100644
--- a/tests/ui/proc-macro/auxiliary/expand-expr.rs
+++ b/tests/ui/proc-macro/auxiliary/expand-expr.rs
@@ -3,9 +3,10 @@
 
 extern crate proc_macro;
 
-use proc_macro::*;
 use std::str::FromStr;
 
+use proc_macro::*;
+
 // Flatten the TokenStream, removing any toplevel `Delimiter::None`s for
 // comparison.
 fn flatten(ts: TokenStream) -> Vec<TokenTree> {
@@ -136,9 +137,8 @@ pub fn check_expand_expr_file(ts: TokenStream) -> TokenStream {
         .to_string();
     assert_eq!(input_t, parse_t);
 
-    // Check that the literal matches `Span::call_site().source_file().path()`
-    let expect_t =
-        Literal::string(&Span::call_site().source_file().path().to_string_lossy()).to_string();
+    // Check that the literal matches `Span::call_site().file()`
+    let expect_t = Literal::string(&Span::call_site().file()).to_string();
     assert_eq!(input_t, expect_t);
 
     TokenStream::new()
diff --git a/tests/ui/proc-macro/auxiliary/macro-only-syntax.rs b/tests/ui/proc-macro/auxiliary/macro-only-syntax.rs
index 4971de284b7..11e1910288e 100644
--- a/tests/ui/proc-macro/auxiliary/macro-only-syntax.rs
+++ b/tests/ui/proc-macro/auxiliary/macro-only-syntax.rs
@@ -79,7 +79,7 @@ fn check_useful_span(token: TokenTree, expected_filename: &str) {
     let span = token.span();
     assert!(span.column() < span.end().column());
 
-    let source_path = span.source_file().path();
+    let source_path = span.local_file().unwrap();
     let filename = source_path.components().last().unwrap();
     assert_eq!(filename, Component::Normal(expected_filename.as_ref()));
 }
diff --git a/tests/ui/proc-macro/auxiliary/span-api-tests.rs b/tests/ui/proc-macro/auxiliary/span-api-tests.rs
index 99db66ed6a9..036f2e3ac3f 100644
--- a/tests/ui/proc-macro/auxiliary/span-api-tests.rs
+++ b/tests/ui/proc-macro/auxiliary/span-api-tests.rs
@@ -11,20 +11,9 @@ pub fn reemit(input: TokenStream) -> TokenStream {
 }
 
 #[proc_macro]
-pub fn assert_fake_source_file(input: TokenStream) -> TokenStream {
+pub fn assert_local_file(input: TokenStream) -> TokenStream {
     for tk in input {
-        let source_file = tk.span().source_file();
-        assert!(!source_file.is_real(), "Source file is real: {:?}", source_file);
-    }
-
-    "".parse().unwrap()
-}
-
-#[proc_macro]
-pub fn assert_source_file(input: TokenStream) -> TokenStream {
-    for tk in input {
-        let source_file = tk.span().source_file();
-        assert!(source_file.is_real(), "Source file is not real: {:?}", source_file);
+        assert!(tk.span().local_file().is_some(), "No local file for span: {:?}", tk.span());
     }
 
     "".parse().unwrap()
diff --git a/tests/ui/proc-macro/span-api-tests.rs b/tests/ui/proc-macro/span-api-tests.rs
index ac42a7ea611..792859ed05b 100644
--- a/tests/ui/proc-macro/span-api-tests.rs
+++ b/tests/ui/proc-macro/span-api-tests.rs
@@ -8,26 +8,24 @@ extern crate span_test_macros;
 
 extern crate span_api_tests;
 
-// FIXME(69775): Investigate `assert_fake_source_file`.
-
-use span_api_tests::{reemit, assert_source_file, macro_stringify};
+use span_api_tests::{reemit, assert_local_file, macro_stringify};
 
 macro_rules! say_hello {
     ($macname:ident) => ( $macname! { "Hello, world!" })
 }
 
-assert_source_file! { "Hello, world!" }
+assert_local_file! { "Hello, world!" }
 
-say_hello! { assert_source_file }
+say_hello! { assert_local_file }
 
 reemit_legacy! {
-    assert_source_file! { "Hello, world!" }
+    assert_local_file! { "Hello, world!" }
 }
 
-say_hello_extern! { assert_source_file }
+say_hello_extern! { assert_local_file }
 
 reemit! {
-    assert_source_file! { "Hello, world!" }
+    assert_local_file! { "Hello, world!" }
 }
 
 fn main() {