Use upstream 'Literal::subspan()'.

This commit is contained in:
Sergio Benitez 2018-11-23 19:15:13 -06:00
parent ed4055925d
commit 543b07a4ba
6 changed files with 53 additions and 56 deletions

View File

@ -8,8 +8,8 @@ use yansi::Color::{Red, Yellow, Blue};
use version_check::{supports_features, is_min_version, is_min_date}; use version_check::{supports_features, is_min_version, is_min_date};
// Specifies the minimum nightly version needed to compile Rocket. // Specifies the minimum nightly version needed to compile Rocket.
const MIN_DATE: &'static str = "2018-10-05"; const MIN_DATE: &'static str = "2018-11-23";
const MIN_VERSION: &'static str = "1.31.0-nightly"; const MIN_VERSION: &'static str = "1.32.0-nightly";
fn main() { fn main() {
let ok_channel = supports_features(); let ok_channel = supports_features();

View File

@ -3,7 +3,7 @@ use proc_macro2::TokenStream as TokenStream2;
use devise::{syn, Spanned, SpanWrapped, Result, FromMeta, ext::TypeExt}; use devise::{syn, Spanned, SpanWrapped, Result, FromMeta, ext::TypeExt};
use indexmap::IndexSet; use indexmap::IndexSet;
use proc_macro_ext::{Diagnostics, SpanExt}; use proc_macro_ext::{Diagnostics, StringLit};
use syn_ext::{syn_to_diag, IdentExt}; use syn_ext::{syn_to_diag, IdentExt};
use self::syn::{Attribute, parse::Parser}; use self::syn::{Attribute, parse::Parser};
@ -426,7 +426,10 @@ fn incomplete_route(
) -> Result<TokenStream> { ) -> Result<TokenStream> {
let method_str = method.to_string().to_lowercase(); let method_str = method.to_string().to_lowercase();
// FIXME(proc_macro): there should be a way to get this `Span`. // FIXME(proc_macro): there should be a way to get this `Span`.
let method_span = Span::call_site().subspan(2..2 + method_str.len()).unwrap(); let method_span = StringLit::new(format!("#[{}]", method), Span::call_site())
.subspan(2..2 + method_str.len())
.unwrap_or(Span::call_site());
let method_ident = syn::Ident::new(&method_str, method_span.into()); let method_ident = syn::Ident::new(&method_str, method_span.into());
let function: syn::ItemFn = syn::parse(input).map_err(syn_to_diag) let function: syn::ItemFn = syn::parse(input).map_err(syn_to_diag)

View File

@ -4,7 +4,7 @@ use devise::syn;
use proc_macro::{Span, Diagnostic}; use proc_macro::{Span, Diagnostic};
use http::route::RouteSegment; use http::route::RouteSegment;
use proc_macro_ext::{SpanExt, Diagnostics, PResult, DResult}; use proc_macro_ext::{Diagnostics, StringLit, PResult, DResult};
crate use http::route::{Error, Kind, Source}; crate use http::route::{Error, Kind, Source};
@ -52,15 +52,16 @@ impl Hash for Segment {
fn subspan(needle: &str, haystack: &str, span: Span) -> Option<Span> { fn subspan(needle: &str, haystack: &str, span: Span) -> Option<Span> {
let index = needle.as_ptr() as usize - haystack.as_ptr() as usize; let index = needle.as_ptr() as usize - haystack.as_ptr() as usize;
span.subspan(index..index + needle.len()) StringLit::new(haystack, span).subspan(index..index + needle.len())
} }
fn trailspan(needle: &str, haystack: &str, span: Span) -> Option<Span> { fn trailspan(needle: &str, haystack: &str, span: Span) -> Option<Span> {
let index = needle.as_ptr() as usize - haystack.as_ptr() as usize; let index = needle.as_ptr() as usize - haystack.as_ptr() as usize;
let lit = StringLit::new(haystack, span);
if needle.as_ptr() as usize > haystack.as_ptr() as usize { if needle.as_ptr() as usize > haystack.as_ptr() as usize {
span.subspan((index - 1)..) lit.subspan((index - 1)..)
} else { } else {
span.subspan(index..) lit.subspan(index..)
} }
} }

View File

@ -4,7 +4,7 @@ use devise::{FromMeta, MetaItem, Result, ext::Split2};
use http::{self, ext::IntoOwned}; use http::{self, ext::IntoOwned};
use attribute::segments::{parse_segments, parse_segment, Segment, Kind, Source}; use attribute::segments::{parse_segments, parse_segment, Segment, Kind, Source};
use proc_macro_ext::SpanExt; use proc_macro_ext::StringLit;
#[derive(Debug)] #[derive(Debug)]
crate struct ContentType(crate http::ContentType); crate struct ContentType(crate http::ContentType);
@ -27,6 +27,12 @@ crate struct DataSegment(crate Segment);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
crate struct Optional<T>(crate Option<T>); crate struct Optional<T>(crate Option<T>);
impl FromMeta for StringLit {
fn from_meta(meta: MetaItem) -> Result<Self> {
Ok(StringLit::new(String::from_meta(meta)?, meta.value_span()))
}
}
#[derive(Debug)] #[derive(Debug)]
crate struct RoutePath { crate struct RoutePath {
crate origin: Origin, crate origin: Origin,
@ -159,14 +165,13 @@ impl ToTokens for Method {
impl FromMeta for Origin { impl FromMeta for Origin {
fn from_meta(meta: MetaItem) -> Result<Self> { fn from_meta(meta: MetaItem) -> Result<Self> {
let string = String::from_meta(meta)?; let string = StringLit::from_meta(meta)?;
let span = meta.value_span();
let uri = http::uri::Origin::parse_route(&string) let uri = http::uri::Origin::parse_route(&string)
.map_err(|e| { .map_err(|e| {
let span = e.index() let span = e.index()
.map(|i| span.subspan(i + 1..).expect("origin")) .map(|i| string.subspan(i + 1..).expect("origin"))
.unwrap_or(span); .unwrap_or(string.span());
span.error(format!("invalid path URI: {}", e)) span.error(format!("invalid path URI: {}", e))
.help("expected path in origin form: \"/path/<param>\"") .help("expected path in origin form: \"/path/<param>\"")
@ -174,7 +179,7 @@ impl FromMeta for Origin {
if !uri.is_normalized() { if !uri.is_normalized() {
let normalized = uri.to_normalized(); let normalized = uri.to_normalized();
return Err(span.error("paths cannot contain empty segments") return Err(string.span().error("paths cannot contain empty segments")
.note(format!("expected '{}', found '{}'", normalized, uri))); .note(format!("expected '{}', found '{}'", normalized, uri)));
} }
@ -184,9 +189,8 @@ impl FromMeta for Origin {
impl FromMeta for Segment { impl FromMeta for Segment {
fn from_meta(meta: MetaItem) -> Result<Self> { fn from_meta(meta: MetaItem) -> Result<Self> {
let string = String::from_meta(meta)?; let string = StringLit::from_meta(meta)?;
let span = meta.value_span() let span = string.subspan(1..(string.len() + 1))
.subspan(1..(string.len() + 1))
.expect("segment"); .expect("segment");
let segment = parse_segment(&string, span)?; let segment = parse_segment(&string, span)?;
@ -210,15 +214,15 @@ impl FromMeta for DataSegment {
impl FromMeta for RoutePath { impl FromMeta for RoutePath {
fn from_meta(meta: MetaItem) -> Result<Self> { fn from_meta(meta: MetaItem) -> Result<Self> {
let (origin, span) = (Origin::from_meta(meta)?, meta.value_span()); let (origin, string) = (Origin::from_meta(meta)?, StringLit::from_meta(meta)?);
let path_span = span.subspan(1..origin.0.path().len() + 1).expect("path"); let path_span = string.subspan(1..origin.0.path().len() + 1).expect("path");
let path = parse_segments(origin.0.path(), '/', Source::Path, path_span); let path = parse_segments(origin.0.path(), '/', Source::Path, path_span);
let query = origin.0.query() let query = origin.0.query()
.map(|q| { .map(|q| {
let len_to_q = 1 + origin.0.path().len() + 1; let len_to_q = 1 + origin.0.path().len() + 1;
let end_of_q = len_to_q + q.len(); let end_of_q = len_to_q + q.len();
let query_span = span.subspan(len_to_q..end_of_q).expect("query"); let query_span = string.subspan(len_to_q..end_of_q).expect("query");
if q.starts_with('&') || q.contains("&&") || q.ends_with('&') { if q.starts_with('&') || q.contains("&&") || q.ends_with('&') {
// TODO: Show a help message with what's expected. // TODO: Show a help message with what's expected.
Err(query_span.error("query cannot contain empty segments").into()) Err(query_span.error("query cannot contain empty segments").into())

View File

@ -60,7 +60,7 @@
//! ``` //! ```
#[macro_use] extern crate quote; #[macro_use] extern crate quote;
#[macro_use] extern crate devise; extern crate devise;
extern crate proc_macro; extern crate proc_macro;
extern crate rocket_http as http; extern crate rocket_http as http;
extern crate indexmap; extern crate indexmap;

View File

@ -1,6 +1,6 @@
use std::ops::{Bound, RangeBounds}; use std::ops::{Bound, RangeBounds};
use proc_macro::{Span, Diagnostic, /* MultiSpan */}; use proc_macro::{Span, Diagnostic, Literal};
use syntax_pos::{Span as InnerSpan, Pos, BytePos}; use syntax_pos::{Span as InnerSpan, Pos, BytePos};
pub type PResult<T> = ::std::result::Result<T, Diagnostic>; pub type PResult<T> = ::std::result::Result<T, Diagnostic>;
@ -62,42 +62,31 @@ impl From<Vec<Diagnostic>> for Diagnostics {
} }
} }
pub trait SpanExt { use std::ops::Deref;
fn subspan<R: RangeBounds<usize>>(self, range: R) -> Option<Span>;
}
impl SpanExt for Span { pub struct StringLit(crate String, crate Literal);
/// Create a `subspan` from `start` to `end`.
fn subspan<R: RangeBounds<usize>>(self, range: R) -> Option<Span> {
let inner: InnerSpan = unsafe { ::std::mem::transmute(self) };
let length = inner.hi().to_usize() - inner.lo().to_usize();
let start = match range.start_bound() { impl Deref for StringLit {
Bound::Included(&lo) => lo, type Target = str;
Bound::Excluded(&lo) => lo + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound() { fn deref(&self) -> &str {
Bound::Included(&hi) => hi + 1, &self.0
Bound::Excluded(&hi) => hi, }
Bound::Unbounded => length, }
};
impl StringLit {
// Bounds check the values, preventing addition overflow and OOB spans. pub fn new<S: Into<String>>(string: S, span: Span) -> Self {
if start > u32::max_value() as usize let string = string.into();
|| end > u32::max_value() as usize let mut lit = Literal::string(&string);
|| (u32::max_value() - start as u32) < inner.lo().to_u32() lit.set_span(span);
|| (u32::max_value() - end as u32) < inner.lo().to_u32() StringLit(string, lit)
|| start >= end }
|| end > length
{ pub fn span(&self) -> Span {
return None; self.1.span()
} }
let new_lo = inner.lo() + BytePos(start as u32); pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
let new_hi = inner.lo() + BytePos(end as u32); self.1.subspan(range)
let new_inner = inner.with_lo(new_lo).with_hi(new_hi);
Some(unsafe { ::std::mem::transmute(new_inner) })
} }
} }