diff --git a/core/codegen/build.rs b/core/codegen/build.rs index 8b798570..548ce9b4 100644 --- a/core/codegen/build.rs +++ b/core/codegen/build.rs @@ -8,8 +8,8 @@ use yansi::Color::{Red, Yellow, Blue}; use version_check::{supports_features, is_min_version, is_min_date}; // Specifies the minimum nightly version needed to compile Rocket. -const MIN_DATE: &'static str = "2018-10-05"; -const MIN_VERSION: &'static str = "1.31.0-nightly"; +const MIN_DATE: &'static str = "2018-11-23"; +const MIN_VERSION: &'static str = "1.32.0-nightly"; fn main() { let ok_channel = supports_features(); diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 57ea7ac5..4757dbc4 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -3,7 +3,7 @@ use proc_macro2::TokenStream as TokenStream2; use devise::{syn, Spanned, SpanWrapped, Result, FromMeta, ext::TypeExt}; use indexmap::IndexSet; -use proc_macro_ext::{Diagnostics, SpanExt}; +use proc_macro_ext::{Diagnostics, StringLit}; use syn_ext::{syn_to_diag, IdentExt}; use self::syn::{Attribute, parse::Parser}; @@ -426,7 +426,10 @@ fn incomplete_route( ) -> Result { let method_str = method.to_string().to_lowercase(); // 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 function: syn::ItemFn = syn::parse(input).map_err(syn_to_diag) diff --git a/core/codegen/src/attribute/segments.rs b/core/codegen/src/attribute/segments.rs index 0e7d0d25..ccbc34d3 100644 --- a/core/codegen/src/attribute/segments.rs +++ b/core/codegen/src/attribute/segments.rs @@ -4,7 +4,7 @@ use devise::syn; use proc_macro::{Span, Diagnostic}; 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}; @@ -52,15 +52,16 @@ impl Hash for Segment { fn subspan(needle: &str, haystack: &str, span: Span) -> Option { 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 { 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 { - span.subspan((index - 1)..) + lit.subspan((index - 1)..) } else { - span.subspan(index..) + lit.subspan(index..) } } diff --git a/core/codegen/src/http_codegen.rs b/core/codegen/src/http_codegen.rs index 27cf702b..08634c63 100644 --- a/core/codegen/src/http_codegen.rs +++ b/core/codegen/src/http_codegen.rs @@ -4,7 +4,7 @@ use devise::{FromMeta, MetaItem, Result, ext::Split2}; use http::{self, ext::IntoOwned}; use attribute::segments::{parse_segments, parse_segment, Segment, Kind, Source}; -use proc_macro_ext::SpanExt; +use proc_macro_ext::StringLit; #[derive(Debug)] crate struct ContentType(crate http::ContentType); @@ -27,6 +27,12 @@ crate struct DataSegment(crate Segment); #[derive(Clone, Debug)] crate struct Optional(crate Option); +impl FromMeta for StringLit { + fn from_meta(meta: MetaItem) -> Result { + Ok(StringLit::new(String::from_meta(meta)?, meta.value_span())) + } +} + #[derive(Debug)] crate struct RoutePath { crate origin: Origin, @@ -159,14 +165,13 @@ impl ToTokens for Method { impl FromMeta for Origin { fn from_meta(meta: MetaItem) -> Result { - let string = String::from_meta(meta)?; - let span = meta.value_span(); + let string = StringLit::from_meta(meta)?; let uri = http::uri::Origin::parse_route(&string) .map_err(|e| { let span = e.index() - .map(|i| span.subspan(i + 1..).expect("origin")) - .unwrap_or(span); + .map(|i| string.subspan(i + 1..).expect("origin")) + .unwrap_or(string.span()); span.error(format!("invalid path URI: {}", e)) .help("expected path in origin form: \"/path/\"") @@ -174,7 +179,7 @@ impl FromMeta for Origin { if !uri.is_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))); } @@ -184,9 +189,8 @@ impl FromMeta for Origin { impl FromMeta for Segment { fn from_meta(meta: MetaItem) -> Result { - let string = String::from_meta(meta)?; - let span = meta.value_span() - .subspan(1..(string.len() + 1)) + let string = StringLit::from_meta(meta)?; + let span = string.subspan(1..(string.len() + 1)) .expect("segment"); let segment = parse_segment(&string, span)?; @@ -210,15 +214,15 @@ impl FromMeta for DataSegment { impl FromMeta for RoutePath { fn from_meta(meta: MetaItem) -> Result { - let (origin, span) = (Origin::from_meta(meta)?, meta.value_span()); - let path_span = span.subspan(1..origin.0.path().len() + 1).expect("path"); + let (origin, string) = (Origin::from_meta(meta)?, StringLit::from_meta(meta)?); + 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 query = origin.0.query() .map(|q| { let len_to_q = 1 + origin.0.path().len() + 1; 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('&') { // TODO: Show a help message with what's expected. Err(query_span.error("query cannot contain empty segments").into()) diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index 3c71ca13..c2b087d1 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -60,7 +60,7 @@ //! ``` #[macro_use] extern crate quote; -#[macro_use] extern crate devise; +extern crate devise; extern crate proc_macro; extern crate rocket_http as http; extern crate indexmap; diff --git a/core/codegen/src/proc_macro_ext.rs b/core/codegen/src/proc_macro_ext.rs index 491f22fc..bbd9f68e 100644 --- a/core/codegen/src/proc_macro_ext.rs +++ b/core/codegen/src/proc_macro_ext.rs @@ -1,6 +1,6 @@ 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}; pub type PResult = ::std::result::Result; @@ -62,42 +62,31 @@ impl From> for Diagnostics { } } -pub trait SpanExt { - fn subspan>(self, range: R) -> Option; -} +use std::ops::Deref; -impl SpanExt for Span { - /// Create a `subspan` from `start` to `end`. - fn subspan>(self, range: R) -> Option { - let inner: InnerSpan = unsafe { ::std::mem::transmute(self) }; - let length = inner.hi().to_usize() - inner.lo().to_usize(); +pub struct StringLit(crate String, crate Literal); - let start = match range.start_bound() { - Bound::Included(&lo) => lo, - Bound::Excluded(&lo) => lo + 1, - Bound::Unbounded => 0, - }; +impl Deref for StringLit { + type Target = str; - let end = match range.end_bound() { - Bound::Included(&hi) => hi + 1, - Bound::Excluded(&hi) => hi, - Bound::Unbounded => length, - }; - - // Bounds check the values, preventing addition overflow and OOB spans. - if start > u32::max_value() as usize - || end > u32::max_value() as usize - || (u32::max_value() - start as u32) < inner.lo().to_u32() - || (u32::max_value() - end as u32) < inner.lo().to_u32() - || start >= end - || end > length - { - return None; - } - - let new_lo = inner.lo() + BytePos(start as u32); - let new_hi = inner.lo() + BytePos(end as u32); - let new_inner = inner.with_lo(new_lo).with_hi(new_hi); - Some(unsafe { ::std::mem::transmute(new_inner) }) + fn deref(&self) -> &str { + &self.0 + } +} + +impl StringLit { + pub fn new>(string: S, span: Span) -> Self { + let string = string.into(); + let mut lit = Literal::string(&string); + lit.set_span(span); + StringLit(string, lit) + } + + pub fn span(&self) -> Span { + self.1.span() + } + + pub fn subspan>(&self, range: R) -> Option { + self.1.subspan(range) } }