Rocket

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit c86f4312fb273c0380fb76a97bfb7fc227800542
parent b9c3a5c64bf20b1b83bfb709d117970156b0fc00
Author: Oliver Scherer <github35764891676564198441@oli-obk.de>
Date:   Tue, 22 Jan 2019 11:03:37 +0100

Always produce a valid, if conservative, subspan.

Diffstat:
Mcore/codegen/src/attribute/route.rs | 3+--
Mcore/codegen/src/attribute/segments.rs | 12++++++------
Mcore/codegen/src/http_codegen.rs | 9++++-----
Mcore/codegen/src/proc_macro_ext.rs | 6++++--
Mcore/codegen/tests/expansion.rs | 18+++++++++++++++++-
5 files changed, 32 insertions(+), 16 deletions(-)

diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs @@ -428,8 +428,7 @@ fn incomplete_route( let method_str = method.to_string().to_lowercase(); // FIXME(proc_macro): there should be a way to get this `Span`. let method_span = StringLit::new(format!("#[{}]", method), Span::call_site()) - .subspan(2..2 + method_str.len()) - .unwrap_or(Span::call_site()); + .subspan(2..2 + method_str.len()); let method_ident = syn::Ident::new(&method_str, method_span.into()); diff --git a/core/codegen/src/attribute/segments.rs b/core/codegen/src/attribute/segments.rs @@ -57,12 +57,12 @@ impl Hash for Segment { } } -fn subspan(needle: &str, haystack: &str, span: Span) -> Option<Span> { +fn subspan(needle: &str, haystack: &str, span: Span) -> Span { let index = needle.as_ptr() as usize - haystack.as_ptr() as usize; 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) -> Span { 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 { @@ -78,7 +78,7 @@ fn into_diagnostic( span: Span, // The `Span` of `Source`. error: &Error, // The error. ) -> Diagnostic { - let seg_span = subspan(segment, source, span).expect("seg_span"); + let seg_span = subspan(segment, source, span); match error { Error::Empty => { seg_span.error("parameter names cannot be empty") @@ -106,8 +106,8 @@ fn into_diagnostic( .help("reserved characters include: '%', '+', '&', etc.") } Error::Trailing(multi) => { - let multi_span = subspan(multi, source, span).expect("mutli_span"); - trailspan(segment, source, span).expect("trailspan") + let multi_span = subspan(multi, source, span); + trailspan(segment, source, span) .error("unexpected trailing text after a '..' param") .help("a multi-segment param must be the final component") .span_note(multi_span, "multi-segment param is here") @@ -140,7 +140,7 @@ crate fn parse_segments<P: UriPart>( break; } } else if let Ok(segment) = result { - let seg_span = subspan(&segment.string, string, span).expect("seg"); + let seg_span = subspan(&segment.string, string, span); segments.push(Segment::from(segment, seg_span)); } } diff --git a/core/codegen/src/http_codegen.rs b/core/codegen/src/http_codegen.rs @@ -172,7 +172,7 @@ impl FromMeta for Origin { let uri = http::uri::Origin::parse_route(&string) .map_err(|e| { let span = e.index() - .map(|i| string.subspan(i + 1..).expect("origin")) + .map(|i| string.subspan(i + 1..)) .unwrap_or(string.span()); span.error(format!("invalid path URI: {}", e)) @@ -192,8 +192,7 @@ impl FromMeta for Origin { impl FromMeta for DataSegment { fn from_meta(meta: MetaItem) -> Result<Self> { let string = StringLit::from_meta(meta)?; - let span = string.subspan(1..(string.len() + 1)) - .expect("segment"); + let span = string.subspan(1..(string.len() + 1)); let segment = parse_data_segment(&string, span)?; if segment.kind != Kind::Single { @@ -208,14 +207,14 @@ impl FromMeta for DataSegment { impl FromMeta for RoutePath { fn from_meta(meta: MetaItem) -> Result<Self> { 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_span = string.subspan(1..origin.0.path().len() + 1); let path = parse_segments::<Path>(origin.0.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 = string.subspan(len_to_q..end_of_q).expect("query"); + let query_span = string.subspan(len_to_q..end_of_q); 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/proc_macro_ext.rs b/core/codegen/src/proc_macro_ext.rs @@ -85,7 +85,9 @@ impl StringLit { self.1.span() } - pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> { - self.1.subspan(range) + /// Attempt to obtain a subspan, or, failing that, produce the full span. + /// This will create suboptimal diagnostics, but better than failing to build entirely. + pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Span { + self.1.subspan(range).unwrap_or_else(|| self.span()) } } diff --git a/core/codegen/tests/expansion.rs b/core/codegen/tests/expansion.rs @@ -20,9 +20,22 @@ macro_rules! make_handler { make_handler!(); + +macro_rules! foo { + ($addr:expr, $name:ident) => { + #[get($addr)] + fn hi($name: String) -> String { + $name + } + }; +} + +// regression test for `#[get] panicking if used inside a macro +foo!("/hello/<name>", name); + #[test] fn test_reexpansion() { - let rocket = rocket::ignite().mount("/", routes![easy, hard]); + let rocket = rocket::ignite().mount("/", routes![easy, hard, hi]); let client = Client::new(rocket).unwrap(); let mut response = client.get("/easy/327").dispatch(); @@ -30,6 +43,9 @@ fn test_reexpansion() { let mut response = client.get("/hard/72").dispatch(); assert_eq!(response.body_string().unwrap(), "hard id: 72"); + + let mut response = client.get("/hello/fish").dispatch(); + assert_eq!(response.body_string().unwrap(), "fish"); } macro_rules! index {