Auto merge of #43604 - abonander:proc_macro_span_api, r=jseyfried
Improvements to `proc_macro::Span` API Motivation: https://internals.rust-lang.org/t/better-panic-location-reporting-for-unwrap-and-friends/5042/12?u=logician TODO: - [x] Bikeshedding/complete API - [x] Implement tests/verify return values cc @jseyfried @nrc
This commit is contained in:
commit
05cbece094
@ -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())
|
||||
|
@ -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() {}
|
Loading…
Reference in New Issue
Block a user