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:
bors 2017-10-06 18:52:30 +00:00
commit 05cbece094
4 changed files with 235 additions and 3 deletions

View File

@ -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())

View File

@ -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()
}

View File

@ -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!" })
}

View 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() {}