proc_macro::Span API improvements
This commit is contained in:
@@ -50,6 +50,7 @@ mod diagnostic;
|
|||||||
pub use diagnostic::{Diagnostic, Level};
|
pub use diagnostic::{Diagnostic, Level};
|
||||||
|
|
||||||
use std::{ascii, fmt, iter};
|
use std::{ascii, fmt, iter};
|
||||||
|
use std::rc::Rc;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
@@ -58,7 +59,7 @@ use syntax::parse::{self, token};
|
|||||||
use syntax::symbol::Symbol;
|
use syntax::symbol::Symbol;
|
||||||
use syntax::tokenstream;
|
use syntax::tokenstream;
|
||||||
use syntax_pos::DUMMY_SP;
|
use syntax_pos::DUMMY_SP;
|
||||||
use syntax_pos::SyntaxContext;
|
use syntax_pos::{FileMap, Pos, SyntaxContext};
|
||||||
use syntax_pos::hygiene::Mark;
|
use syntax_pos::hygiene::Mark;
|
||||||
|
|
||||||
/// The main type provided by this crate, representing an abstract stream of
|
/// 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.
|
/// A region of source code, along with macro expansion information.
|
||||||
#[unstable(feature = "proc_macro", issue = "38356")]
|
#[unstable(feature = "proc_macro", issue = "38356")]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Span(syntax_pos::Span);
|
pub struct Span(syntax_pos::Span);
|
||||||
|
|
||||||
#[unstable(feature = "proc_macro", issue = "38356")]
|
#[unstable(feature = "proc_macro", issue = "38356")]
|
||||||
@@ -211,12 +212,132 @@ impl Span {
|
|||||||
::__internal::with_sess(|(_, mark)| Span(mark.expn_info().unwrap().call_site))
|
::__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!(error, Level::Error);
|
||||||
diagnostic_method!(warning, Level::Warning);
|
diagnostic_method!(warning, Level::Warning);
|
||||||
diagnostic_method!(note, Level::Note);
|
diagnostic_method!(note, Level::Note);
|
||||||
diagnostic_method!(help, Level::Help);
|
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, (), ..]`).
|
/// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`).
|
||||||
#[unstable(feature = "proc_macro", issue = "38356")]
|
#[unstable(feature = "proc_macro", issue = "38356")]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -618,10 +739,14 @@ pub mod __internal {
|
|||||||
use syntax::parse::{self, ParseSess};
|
use syntax::parse::{self, ParseSess};
|
||||||
use syntax::parse::token::{self, Token};
|
use syntax::parse::token::{self, Token};
|
||||||
use syntax::tokenstream;
|
use syntax::tokenstream;
|
||||||
use syntax_pos::DUMMY_SP;
|
use syntax_pos::{BytePos, Loc, DUMMY_SP};
|
||||||
|
|
||||||
use super::{TokenStream, LexError};
|
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 {
|
pub fn new_token_stream(item: P<ast::Item>) -> TokenStream {
|
||||||
let token = Token::interpolated(token::NtItem(item));
|
let token = Token::interpolated(token::NtItem(item));
|
||||||
TokenStream(tokenstream::TokenTree::Token(DUMMY_SP, token).into())
|
TokenStream(tokenstream::TokenTree::Token(DUMMY_SP, token).into())
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -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!" })
|
||||||
|
}
|
||||||
43
src/test/run-pass-fulldeps/proc-macro/span-api-tests.rs
Normal file
43
src/test/run-pass-fulldeps/proc-macro/span-api-tests.rs
Normal file
@@ -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() {}
|
||||||
Reference in New Issue
Block a user