Skip to content

Commit 7be36d2

Browse files
committed
proc_macro::Span API improvements
1 parent c6884b1 commit 7be36d2

File tree

4 files changed

+235
-3
lines changed

4 files changed

+235
-3
lines changed

src/libproc_macro/lib.rs

+128-3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ mod diagnostic;
5050
pub use diagnostic::{Diagnostic, Level};
5151

5252
use std::{ascii, fmt, iter};
53+
use std::rc::Rc;
5354
use std::str::FromStr;
5455

5556
use syntax::ast;
@@ -58,7 +59,7 @@ use syntax::parse::{self, token};
5859
use syntax::symbol::Symbol;
5960
use syntax::tokenstream;
6061
use syntax_pos::DUMMY_SP;
61-
use syntax_pos::SyntaxContext;
62+
use syntax_pos::{FileMap, Pos, SyntaxContext};
6263
use syntax_pos::hygiene::Mark;
6364

6465
/// The main type provided by this crate, representing an abstract stream of
@@ -173,7 +174,7 @@ impl TokenStream {
173174

174175
/// A region of source code, along with macro expansion information.
175176
#[unstable(feature = "proc_macro", issue = "38356")]
176-
#[derive(Copy, Clone, Debug)]
177+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
177178
pub struct Span(syntax_pos::Span);
178179

179180
#[unstable(feature = "proc_macro", issue = "38356")]
@@ -211,12 +212,132 @@ impl Span {
211212
::__internal::with_sess(|(_, mark)| Span(mark.expn_info().unwrap().call_site))
212213
}
213214

215+
/// The original source file into which this span points.
216+
#[unstable(feature = "proc_macro", issue = "38356")]
217+
pub fn source_file(&self) -> SourceFile {
218+
SourceFile {
219+
filemap: __internal::lookup_char_pos(self.0.lo()).file,
220+
}
221+
}
222+
223+
/// Get the starting line/column in the source file for this span.
224+
#[unstable(feature = "proc_macro", issue = "38356")]
225+
pub fn start(&self) -> LineColumn {
226+
let loc = __internal::lookup_char_pos(self.0.lo());
227+
LineColumn {
228+
line: loc.line,
229+
column: loc.col.to_usize()
230+
}
231+
}
232+
233+
/// Get the ending line/column in the source file for this span.
234+
#[unstable(feature = "proc_macro", issue = "38356")]
235+
pub fn end(&self) -> LineColumn {
236+
let loc = __internal::lookup_char_pos(self.0.hi());
237+
LineColumn {
238+
line: loc.line,
239+
column: loc.col.to_usize()
240+
}
241+
}
242+
243+
/// Create a new span encompassing `self` and `other`.
244+
///
245+
/// Returns `None` if `self` and `other` are from different files.
246+
#[unstable(feature = "proc_macro", issue = "38356")]
247+
pub fn join(&self, other: Span) -> Option<Span> {
248+
let self_loc = __internal::lookup_char_pos(self.0.lo());
249+
let other_loc = __internal::lookup_char_pos(self.0.lo());
250+
251+
if self_loc.file.name != other_loc.file.name { return None }
252+
253+
Some(Span(self.0.to(other.0)))
254+
}
255+
214256
diagnostic_method!(error, Level::Error);
215257
diagnostic_method!(warning, Level::Warning);
216258
diagnostic_method!(note, Level::Note);
217259
diagnostic_method!(help, Level::Help);
218260
}
219261

262+
/// A line-column pair representing the start or end of a `Span`.
263+
#[unstable(feature = "proc_macro", issue = "38356")]
264+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
265+
pub struct LineColumn {
266+
/// The 1-indexed line in the source file on which the span starts or ends (inclusive).
267+
line: usize,
268+
/// The 0-indexed column (in UTF-8 characters) in the source file on which
269+
/// the span starts or ends (inclusive).
270+
column: usize
271+
}
272+
273+
/// The source file of a given `Span`.
274+
#[unstable(feature = "proc_macro", issue = "38356")]
275+
#[derive(Clone)]
276+
pub struct SourceFile {
277+
filemap: Rc<FileMap>,
278+
}
279+
280+
impl SourceFile {
281+
/// Get the path to this source file as a string.
282+
///
283+
/// ### Note
284+
/// If the code span associated with this `SourceFile` was generated by an external macro, this
285+
/// may not be an actual path on the filesystem. Use [`is_real`] to check.
286+
///
287+
/// Also note that even if `is_real` returns `true`, if `-Z remap-path-prefix-*` was passed on
288+
/// the command line, the path as given may not actually be valid.
289+
///
290+
/// [`is_real`]: #method.is_real
291+
# [unstable(feature = "proc_macro", issue = "38356")]
292+
pub fn as_str(&self) -> &str {
293+
&self.filemap.name
294+
}
295+
296+
/// Returns `true` if this source file is a real source file, and not generated by an external
297+
/// macro's expansion.
298+
# [unstable(feature = "proc_macro", issue = "38356")]
299+
pub fn is_real(&self) -> bool {
300+
// This is a hack until intercrate spans are implemented and we can have real source files
301+
// for spans generated in external macros.
302+
// https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368
303+
self.filemap.is_real_file()
304+
}
305+
}
306+
307+
#[unstable(feature = "proc_macro", issue = "38356")]
308+
impl AsRef<str> for SourceFile {
309+
fn as_ref(&self) -> &str {
310+
self.as_str()
311+
}
312+
}
313+
314+
#[unstable(feature = "proc_macro", issue = "38356")]
315+
impl fmt::Debug for SourceFile {
316+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317+
f.debug_struct("SourceFile")
318+
.field("path", &self.as_str())
319+
.field("is_real", &self.is_real())
320+
.finish()
321+
}
322+
}
323+
324+
#[unstable(feature = "proc_macro", issue = "38356")]
325+
impl PartialEq for SourceFile {
326+
fn eq(&self, other: &Self) -> bool {
327+
Rc::ptr_eq(&self.filemap, &other.filemap)
328+
}
329+
}
330+
331+
#[unstable(feature = "proc_macro", issue = "38356")]
332+
impl Eq for SourceFile {}
333+
334+
#[unstable(feature = "proc_macro", issue = "38356")]
335+
impl PartialEq<str> for SourceFile {
336+
fn eq(&self, other: &str) -> bool {
337+
self.as_ref() == other
338+
}
339+
}
340+
220341
/// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`).
221342
#[unstable(feature = "proc_macro", issue = "38356")]
222343
#[derive(Clone, Debug)]
@@ -618,10 +739,14 @@ pub mod __internal {
618739
use syntax::parse::{self, ParseSess};
619740
use syntax::parse::token::{self, Token};
620741
use syntax::tokenstream;
621-
use syntax_pos::DUMMY_SP;
742+
use syntax_pos::{BytePos, Loc, DUMMY_SP};
622743

623744
use super::{TokenStream, LexError};
624745

746+
pub fn lookup_char_pos(pos: BytePos) -> Loc {
747+
with_sess(|(sess, _)| sess.codemap().lookup_char_pos(pos))
748+
}
749+
625750
pub fn new_token_stream(item: P<ast::Item>) -> TokenStream {
626751
let token = Token::interpolated(token::NtItem(item));
627752
TokenStream(tokenstream::TokenTree::Token(DUMMY_SP, token).into())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// force-host
12+
// no-prefer-dynamic
13+
14+
#![crate_type = "proc-macro"]
15+
#![feature(proc_macro)]
16+
17+
extern crate proc_macro;
18+
19+
use proc_macro::*;
20+
21+
// Re-emits the input tokens by parsing them from strings
22+
#[proc_macro]
23+
pub fn reemit(input: TokenStream) -> TokenStream {
24+
input.to_string().parse().unwrap()
25+
}
26+
27+
#[proc_macro]
28+
pub fn assert_fake_source_file(input: TokenStream) -> TokenStream {
29+
for tk in input {
30+
let source_file = tk.span.source_file();
31+
assert!(!source_file.is_real(), "Source file is real: {:?}", source_file);
32+
}
33+
34+
"".parse().unwrap()
35+
}
36+
37+
#[proc_macro]
38+
pub fn assert_source_file(input: TokenStream) -> TokenStream {
39+
for tk in input {
40+
let source_file = tk.span.source_file();
41+
assert!(source_file.is_real(), "Source file is not real: {:?}", source_file);
42+
}
43+
44+
"".parse().unwrap()
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[macro_export]
12+
macro_rules! reemit_legacy {
13+
($($tok:tt)*) => ($($tok)*)
14+
}
15+
16+
#[macro_export]
17+
macro_rules! say_hello_extern {
18+
($macname:ident) => ( $macname! { "Hello, world!" })
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:span-api-tests.rs
12+
// aux-build:span-test-macros.rs
13+
14+
// ignore-pretty
15+
16+
#![feature(proc_macro)]
17+
18+
#[macro_use]
19+
extern crate span_test_macros;
20+
21+
extern crate span_api_tests;
22+
23+
use span_api_tests::{reemit, assert_fake_source_file, assert_source_file};
24+
25+
macro_rules! say_hello {
26+
($macname:ident) => ( $macname! { "Hello, world!" })
27+
}
28+
29+
assert_source_file! { "Hello, world!" }
30+
31+
say_hello! { assert_source_file }
32+
33+
reemit_legacy! {
34+
assert_source_file! { "Hello, world!" }
35+
}
36+
37+
say_hello_extern! { assert_fake_source_file }
38+
39+
reemit! {
40+
assert_source_file! { "Hello, world!" }
41+
}
42+
43+
fn main() {}

0 commit comments

Comments
 (0)