about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAustin Bonander <austin.bonander@gmail.com>2017-08-01 18:05:08 -0700
committerAustin Bonander <austin.bonander@gmail.com>2017-10-05 17:00:55 -0700
commit7be36d2a6d8855c4ca8ef7ae469f9aac8bf6e63b (patch)
treeb02699f2441b596ff5d4e0279ac65576a2a090f8
parentc6884b12d9298f1e0af919cb9458a0d80acb3b0a (diff)
downloadrust-7be36d2a6d8855c4ca8ef7ae469f9aac8bf6e63b.tar.gz
rust-7be36d2a6d8855c4ca8ef7ae469f9aac8bf6e63b.zip
`proc_macro::Span` API improvements
-rw-r--r--src/libproc_macro/lib.rs131
-rw-r--r--src/test/run-pass-fulldeps/proc-macro/auxiliary/span-api-tests.rs45
-rw-r--r--src/test/run-pass-fulldeps/proc-macro/auxiliary/span-test-macros.rs19
-rw-r--r--src/test/run-pass-fulldeps/proc-macro/span-api-tests.rs43
4 files changed, 235 insertions, 3 deletions
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index 2c540c8de8f..e6307f10c13 100644
--- a/src/libproc_macro/lib.rs
+++ b/src/libproc_macro/lib.rs
@@ -50,6 +50,7 @@ mod diagnostic;
 pub use diagnostic::{Diagnostic, Level};
 
 use std::{ascii, fmt, iter};
+use std::rc::Rc;
 use std::str::FromStr;
 
 use syntax::ast;
@@ -58,7 +59,7 @@ use syntax::parse::{self, token};
 use syntax::symbol::Symbol;
 use syntax::tokenstream;
 use syntax_pos::DUMMY_SP;
-use syntax_pos::SyntaxContext;
+use syntax_pos::{FileMap, Pos, SyntaxContext};
 use syntax_pos::hygiene::Mark;
 
 /// The main type provided by this crate, representing an abstract stream of
@@ -173,7 +174,7 @@ impl TokenStream {
 
 /// A region of source code, along with macro expansion information.
 #[unstable(feature = "proc_macro", issue = "38356")]
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub struct Span(syntax_pos::Span);
 
 #[unstable(feature = "proc_macro", issue = "38356")]
@@ -211,12 +212,132 @@ impl Span {
         ::__internal::with_sess(|(_, mark)| Span(mark.expn_info().unwrap().call_site))
     }
 
+    /// The original source file into which this span points.
+    #[unstable(feature = "proc_macro", issue = "38356")]
+    pub fn source_file(&self) -> SourceFile {
+        SourceFile {
+            filemap: __internal::lookup_char_pos(self.0.lo()).file,
+        }
+    }
+
+    /// Get the starting line/column in the source file for this span.
+    #[unstable(feature = "proc_macro", issue = "38356")]
+    pub fn start(&self) -> LineColumn {
+        let loc = __internal::lookup_char_pos(self.0.lo());
+        LineColumn {
+            line: loc.line,
+            column: loc.col.to_usize()
+        }
+    }
+
+    /// Get the ending line/column in the source file for this span.
+    #[unstable(feature = "proc_macro", issue = "38356")]
+    pub fn end(&self) -> LineColumn {
+        let loc = __internal::lookup_char_pos(self.0.hi());
+        LineColumn {
+            line: loc.line,
+            column: loc.col.to_usize()
+        }
+    }
+
+    /// Create a new span encompassing `self` and `other`.
+    ///
+    /// Returns `None` if `self` and `other` are from different files.
+    #[unstable(feature = "proc_macro", issue = "38356")]
+    pub fn join(&self, other: Span) -> Option<Span> {
+        let self_loc = __internal::lookup_char_pos(self.0.lo());
+        let other_loc = __internal::lookup_char_pos(self.0.lo());
+
+        if self_loc.file.name != other_loc.file.name { return None }
+
+        Some(Span(self.0.to(other.0)))
+    }
+
     diagnostic_method!(error, Level::Error);
     diagnostic_method!(warning, Level::Warning);
     diagnostic_method!(note, Level::Note);
     diagnostic_method!(help, Level::Help);
 }
 
+/// A line-column pair representing the start or end of a `Span`.
+#[unstable(feature = "proc_macro", issue = "38356")]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct LineColumn {
+    /// The 1-indexed line in the source file on which the span starts or ends (inclusive).
+    line: usize,
+    /// The 0-indexed column (in UTF-8 characters) in the source file on which
+    /// the span starts or ends (inclusive).
+    column: usize
+}
+
+/// The source file of a given `Span`.
+#[unstable(feature = "proc_macro", issue = "38356")]
+#[derive(Clone)]
+pub struct SourceFile {
+    filemap: Rc<FileMap>,
+}
+
+impl SourceFile {
+    /// Get the path to this source file as a string.
+    ///
+    /// ### Note
+    /// If the code span associated with this `SourceFile` was generated by an external macro, this
+    /// may not be an actual path on the filesystem. Use [`is_real`] to check.
+    ///
+    /// Also note that even if `is_real` returns `true`, if `-Z remap-path-prefix-*` was passed on
+    /// the command line, the path as given may not actually be valid.
+    ///
+    /// [`is_real`]: #method.is_real
+    # [unstable(feature = "proc_macro", issue = "38356")]
+    pub fn as_str(&self) -> &str {
+        &self.filemap.name
+    }
+
+    /// Returns `true` if this source file is a real source file, and not generated by an external
+    /// macro's expansion.
+    # [unstable(feature = "proc_macro", issue = "38356")]
+    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.filemap.is_real_file()
+    }
+}
+
+#[unstable(feature = "proc_macro", issue = "38356")]
+impl AsRef<str> for SourceFile {
+    fn as_ref(&self) -> &str {
+        self.as_str()
+    }
+}
+
+#[unstable(feature = "proc_macro", issue = "38356")]
+impl fmt::Debug for SourceFile {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("SourceFile")
+            .field("path", &self.as_str())
+            .field("is_real", &self.is_real())
+            .finish()
+    }
+}
+
+#[unstable(feature = "proc_macro", issue = "38356")]
+impl PartialEq for SourceFile {
+    fn eq(&self, other: &Self) -> bool {
+        Rc::ptr_eq(&self.filemap, &other.filemap)
+    }
+}
+
+#[unstable(feature = "proc_macro", issue = "38356")]
+impl Eq for SourceFile {}
+
+#[unstable(feature = "proc_macro", issue = "38356")]
+impl PartialEq<str> for SourceFile {
+    fn eq(&self, other: &str) -> bool {
+        self.as_ref() == other
+    }
+}
+
 /// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`).
 #[unstable(feature = "proc_macro", issue = "38356")]
 #[derive(Clone, Debug)]
@@ -618,10 +739,14 @@ pub mod __internal {
     use syntax::parse::{self, ParseSess};
     use syntax::parse::token::{self, Token};
     use syntax::tokenstream;
-    use syntax_pos::DUMMY_SP;
+    use syntax_pos::{BytePos, Loc, DUMMY_SP};
 
     use super::{TokenStream, LexError};
 
+    pub fn lookup_char_pos(pos: BytePos) -> Loc {
+        with_sess(|(sess, _)| sess.codemap().lookup_char_pos(pos))
+    }
+
     pub fn new_token_stream(item: P<ast::Item>) -> TokenStream {
         let token = Token::interpolated(token::NtItem(item));
         TokenStream(tokenstream::TokenTree::Token(DUMMY_SP, token).into())
diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-api-tests.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-api-tests.rs
new file mode 100644
index 00000000000..ce6ffcc3cb0
--- /dev/null
+++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-api-tests.rs
@@ -0,0 +1,45 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(proc_macro)]
+
+extern crate proc_macro;
+
+use proc_macro::*;
+
+// Re-emits the input tokens by parsing them from strings
+#[proc_macro]
+pub fn reemit(input: TokenStream) -> TokenStream {
+    input.to_string().parse().unwrap()
+}
+
+#[proc_macro]
+pub fn assert_fake_source_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);
+    }
+
+    "".parse().unwrap()
+}
diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-test-macros.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-test-macros.rs
new file mode 100644
index 00000000000..b4666e2cb61
--- /dev/null
+++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/span-test-macros.rs
@@ -0,0 +1,19 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[macro_export]
+macro_rules! reemit_legacy {
+    ($($tok:tt)*) => ($($tok)*)
+}
+
+#[macro_export]
+macro_rules! say_hello_extern {
+    ($macname:ident) => ( $macname! { "Hello, world!" })
+}
diff --git a/src/test/run-pass-fulldeps/proc-macro/span-api-tests.rs b/src/test/run-pass-fulldeps/proc-macro/span-api-tests.rs
new file mode 100644
index 00000000000..c2df561b43a
--- /dev/null
+++ b/src/test/run-pass-fulldeps/proc-macro/span-api-tests.rs
@@ -0,0 +1,43 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:span-api-tests.rs
+// aux-build:span-test-macros.rs
+
+// ignore-pretty
+
+#![feature(proc_macro)]
+
+#[macro_use]
+extern crate span_test_macros;
+
+extern crate span_api_tests;
+
+use span_api_tests::{reemit, assert_fake_source_file, assert_source_file};
+
+macro_rules! say_hello {
+    ($macname:ident) => ( $macname! { "Hello, world!" })
+}
+
+assert_source_file! { "Hello, world!" }
+
+say_hello! { assert_source_file }
+
+reemit_legacy! {
+    assert_source_file! { "Hello, world!" }
+}
+
+say_hello_extern! { assert_fake_source_file }
+
+reemit! {
+    assert_source_file! { "Hello, world!" }
+}
+
+fn main() {}