Make use of SpanRange instead of Span

This commit is contained in:
CreepySkeleton 2020-08-31 11:57:55 +03:00
parent ef120ab2d0
commit 7d16af9a06
3 changed files with 82 additions and 89 deletions

View file

@ -1,18 +1,19 @@
use proc_macro2::{Span, TokenStream, TokenTree}; use proc_macro2::{TokenStream, TokenTree};
use proc_macro_error::SpanRange;
#[derive(Debug)] #[derive(Debug)]
pub enum Markup { pub enum Markup {
Block(Block), Block(Block),
Literal { Literal {
content: String, content: String,
span: Span, span: SpanRange,
}, },
Symbol { Symbol {
symbol: TokenStream, symbol: TokenStream,
}, },
Splice { Splice {
expr: TokenStream, expr: TokenStream,
outer_span: Span, outer_span: SpanRange,
}, },
Element { Element {
name: TokenStream, name: TokenStream,
@ -20,22 +21,22 @@ pub enum Markup {
body: ElementBody, body: ElementBody,
}, },
Let { Let {
at_span: Span, at_span: SpanRange,
tokens: TokenStream, tokens: TokenStream,
}, },
Special { Special {
segments: Vec<Special>, segments: Vec<Special>,
}, },
Match { Match {
at_span: Span, at_span: SpanRange,
head: TokenStream, head: TokenStream,
arms: Vec<MatchArm>, arms: Vec<MatchArm>,
arms_span: Span, arms_span: SpanRange,
}, },
} }
impl Markup { impl Markup {
pub fn span(&self) -> Span { pub fn span(&self) -> SpanRange {
match *self { match *self {
Markup::Block(ref block) => block.span(), Markup::Block(ref block) => block.span(),
Markup::Literal { span, .. } => span, Markup::Literal { span, .. } => span,
@ -43,16 +44,16 @@ impl Markup {
Markup::Splice { outer_span, .. } => outer_span, Markup::Splice { outer_span, .. } => outer_span,
Markup::Element { ref name, ref body, .. } => { Markup::Element { ref name, ref body, .. } => {
let name_span = span_tokens(name.clone()); let name_span = span_tokens(name.clone());
name_span.join(body.span()).unwrap_or(name_span) name_span.join_range(body.span())
}, },
Markup::Let { at_span, ref tokens } => { Markup::Let { at_span, ref tokens } => {
at_span.join(span_tokens(tokens.clone())).unwrap_or(at_span) at_span.join_range(span_tokens(tokens.clone()))
}, },
Markup::Special { ref segments } => { Markup::Special { ref segments } => {
join_spans(segments.iter().map(Special::span)) join_ranges(segments.iter().map(Special::span))
}, },
Markup::Match { at_span, arms_span, .. } => { Markup::Match { at_span, arms_span, .. } => {
at_span.join(arms_span).unwrap_or(at_span) at_span.join_range(arms_span)
}, },
} }
} }
@ -63,12 +64,12 @@ pub type Attrs = Vec<Attr>;
#[derive(Debug)] #[derive(Debug)]
pub enum Attr { pub enum Attr {
Class { Class {
dot_span: Span, dot_span: SpanRange,
name: Markup, name: Markup,
toggler: Option<Toggler>, toggler: Option<Toggler>,
}, },
Id { Id {
hash_span: Span, hash_span: SpanRange,
name: Markup, name: Markup,
}, },
Attribute { Attribute {
@ -77,20 +78,20 @@ pub enum Attr {
} }
impl Attr { impl Attr {
pub fn span(&self) -> Span { pub fn span(&self) -> SpanRange {
match *self { match *self {
Attr::Class { dot_span, ref name, ref toggler } => { Attr::Class { dot_span, ref name, ref toggler } => {
let name_span = name.span(); let name_span = name.span();
let dot_name_span = dot_span.join(name_span).unwrap_or(dot_span); let dot_name_span = dot_span.join_range(name_span);
if let Some(toggler) = toggler { if let Some(toggler) = toggler {
dot_name_span.join(toggler.cond_span).unwrap_or(name_span) dot_name_span.join_range(toggler.cond_span)
} else { } else {
dot_name_span dot_name_span
} }
}, },
Attr::Id { hash_span, ref name } => { Attr::Id { hash_span, ref name } => {
let name_span = name.span(); let name_span = name.span();
hash_span.join(name_span).unwrap_or(hash_span) hash_span.join_range(name_span)
}, },
Attr::Attribute { ref attribute } => attribute.span(), Attr::Attribute { ref attribute } => attribute.span(),
} }
@ -99,12 +100,12 @@ impl Attr {
#[derive(Debug)] #[derive(Debug)]
pub enum ElementBody { pub enum ElementBody {
Void { semi_span: Span }, Void { semi_span: SpanRange },
Block { block: Block }, Block { block: Block },
} }
impl ElementBody { impl ElementBody {
pub fn span(&self) -> Span { pub fn span(&self) -> SpanRange {
match *self { match *self {
ElementBody::Void { semi_span } => semi_span, ElementBody::Void { semi_span } => semi_span,
ElementBody::Block { ref block } => block.span(), ElementBody::Block { ref block } => block.span(),
@ -115,26 +116,26 @@ impl ElementBody {
#[derive(Debug)] #[derive(Debug)]
pub struct Block { pub struct Block {
pub markups: Vec<Markup>, pub markups: Vec<Markup>,
pub outer_span: Span, pub outer_span: SpanRange,
} }
impl Block { impl Block {
pub fn span(&self) -> Span { pub fn span(&self) -> SpanRange {
self.outer_span self.outer_span
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Special { pub struct Special {
pub at_span: Span, pub at_span: SpanRange,
pub head: TokenStream, pub head: TokenStream,
pub body: Block, pub body: Block,
} }
impl Special { impl Special {
pub fn span(&self) -> Span { pub fn span(&self) -> SpanRange {
let body_span = self.body.span(); let body_span = self.body.span();
self.at_span.join(body_span).unwrap_or(self.at_span) self.at_span.join_range(body_span)
} }
} }
@ -145,10 +146,10 @@ pub struct Attribute {
} }
impl Attribute { impl Attribute {
fn span(&self) -> Span { fn span(&self) -> SpanRange {
let name_span = span_tokens(self.name.clone()); let name_span = span_tokens(self.name.clone());
if let Some(attr_type_span) = self.attr_type.span() { if let Some(attr_type_span) = self.attr_type.span() {
name_span.join(attr_type_span).unwrap_or(name_span) name_span.join_range(attr_type_span)
} else { } else {
name_span name_span
} }
@ -166,7 +167,7 @@ pub enum AttrType {
} }
impl AttrType { impl AttrType {
fn span(&self) -> Option<Span> { fn span(&self) -> Option<SpanRange> {
match *self { match *self {
AttrType::Normal { ref value } => Some(value.span()), AttrType::Normal { ref value } => Some(value.span()),
AttrType::Empty { ref toggler } => toggler.as_ref().map(Toggler::span), AttrType::Empty { ref toggler } => toggler.as_ref().map(Toggler::span),
@ -177,11 +178,11 @@ impl AttrType {
#[derive(Debug)] #[derive(Debug)]
pub struct Toggler { pub struct Toggler {
pub cond: TokenStream, pub cond: TokenStream,
pub cond_span: Span, pub cond_span: SpanRange,
} }
impl Toggler { impl Toggler {
fn span(&self) -> Span { fn span(&self) -> SpanRange {
self.cond_span self.cond_span
} }
} }
@ -192,20 +193,16 @@ pub struct MatchArm {
pub body: Block, pub body: Block,
} }
pub fn span_tokens<I: IntoIterator<Item=TokenTree>>(tokens: I) -> Span { pub fn span_tokens<I: IntoIterator<Item=TokenTree>>(tokens: I) -> SpanRange {
join_spans(tokens.into_iter().map(|token| token.span())) join_ranges(tokens.into_iter().map(|s| SpanRange::single_span(s.span())))
} }
pub fn join_spans<I: IntoIterator<Item=Span>>(spans: I) -> Span { pub fn join_ranges<I: IntoIterator<Item=SpanRange>>(ranges: I) -> SpanRange {
let mut iter = spans.into_iter(); let mut iter = ranges.into_iter();
let mut span = match iter.next() { let first = match iter.next() {
Some(span) => span, Some(span) => span,
None => return Span::call_site(), None => return SpanRange::call_site(),
}; };
for new_span in iter { let last = iter.last().unwrap_or(first);
if let Some(joined) = span.join(new_span) { first.join_range(last)
span = joined;
}
}
span
} }

View file

@ -9,6 +9,7 @@ use proc_macro2::{
TokenTree, TokenTree,
}; };
use quote::quote; use quote::quote;
use proc_macro_error::SpanRange;
use crate::ast::*; use crate::ast::*;
@ -63,7 +64,7 @@ impl Generator {
.map(|arm| self.match_arm(arm)) .map(|arm| self.match_arm(arm))
.collect(); .collect();
let mut body = TokenTree::Group(Group::new(Delimiter::Brace, body)); let mut body = TokenTree::Group(Group::new(Delimiter::Brace, body));
body.set_span(arms_span.into()); body.set_span(arms_span.collapse());
let head: TokenStream = head.into(); let head: TokenStream = head.into();
quote!(#head #body) quote!(#head #body)
}); });
@ -75,7 +76,7 @@ impl Generator {
let mut build = self.builder(); let mut build = self.builder();
self.markups(markups, &mut build); self.markups(markups, &mut build);
let mut block = TokenTree::Group(Group::new(Delimiter::Brace, build.finish())); let mut block = TokenTree::Group(Group::new(Delimiter::Brace, build.finish()));
block.set_span(outer_span.into()); block.set_span(outer_span.collapse());
TokenStream::from(block) TokenStream::from(block)
} }
@ -203,7 +204,7 @@ fn desugar_classes_or_ids(
}; };
let head = desugar_toggler(toggler); let head = desugar_toggler(toggler);
markups.push(Markup::Special { markups.push(Markup::Special {
segments: vec![Special { at_span: Span::call_site(), head: head.into(), body }], segments: vec![Special { at_span: SpanRange::call_site(), head: head.into(), body }],
}); });
} }
Some(Attribute { Some(Attribute {
@ -211,7 +212,7 @@ fn desugar_classes_or_ids(
attr_type: AttrType::Normal { attr_type: AttrType::Normal {
value: Markup::Block(Block { value: Markup::Block(Block {
markups, markups,
outer_span: Span::call_site(), outer_span: SpanRange::call_site(),
}), }),
}, },
}) })
@ -236,7 +237,7 @@ fn desugar_toggler(Toggler { cond, cond_span }: Toggler) -> TokenStream {
// wrap it in parentheses to avoid parse errors // wrap it in parentheses to avoid parse errors
if cond.clone().into_iter().any(is_braced_block) { if cond.clone().into_iter().any(is_braced_block) {
let mut wrapped_cond = TokenTree::Group(Group::new(Delimiter::Parenthesis, cond)); let mut wrapped_cond = TokenTree::Group(Group::new(Delimiter::Parenthesis, cond));
wrapped_cond.set_span(cond_span.into()); wrapped_cond.set_span(cond_span.collapse());
cond = TokenStream::from(wrapped_cond); cond = TokenStream::from(wrapped_cond);
} }
quote!(if #cond) quote!(if #cond)

View file

@ -7,7 +7,7 @@ use proc_macro2::{
TokenStream, TokenStream,
TokenTree, TokenTree,
}; };
use proc_macro_error::{abort, abort_call_site}; use proc_macro_error::{abort, abort_call_site, SpanRange};
use std::collections::HashMap; use std::collections::HashMap;
use std::mem; use std::mem;
@ -128,13 +128,11 @@ impl Parser {
"for" => self.for_expr(at_span, keyword), "for" => self.for_expr(at_span, keyword),
"match" => self.match_expr(at_span, keyword), "match" => self.match_expr(at_span, keyword),
"let" => { "let" => {
let ident_span = ident.span(); let span = SpanRange { first: at_span, last: ident.span() };
let span = at_span.join(ident_span).unwrap_or(ident_span);
abort!(span, "`@let` only works inside a block"); abort!(span, "`@let` only works inside a block");
}, },
other => { other => {
let ident_span = ident.span(); let span = SpanRange { first: at_span, last: ident.span() };
let span = at_span.join(ident_span).unwrap_or(ident_span);
abort!(span, "unknown keyword `@{}`", other); abort!(span, "unknown keyword `@{}`", other);
} }
} }
@ -146,7 +144,7 @@ impl Parser {
}, },
// Element // Element
TokenTree::Ident(ident) => { TokenTree::Ident(ident) => {
let ident_string = ident.to_string(); let _ident_string = ident.to_string();
// Is this a keyword that's missing a '@'? // Is this a keyword that's missing a '@'?
// TODO: warning or error? // TODO: warning or error?
// match ident_string.as_str() { // match ident_string.as_str() {
@ -171,12 +169,12 @@ impl Parser {
// Splice // Splice
TokenTree::Group(ref group) if group.delimiter() == Delimiter::Parenthesis => { TokenTree::Group(ref group) if group.delimiter() == Delimiter::Parenthesis => {
self.advance(); self.advance();
ast::Markup::Splice { expr: group.stream(), outer_span: group.span() } ast::Markup::Splice { expr: group.stream(), outer_span: SpanRange::single_span(group.span()) }
} }
// Block // Block
TokenTree::Group(ref group) if group.delimiter() == Delimiter::Brace => { TokenTree::Group(ref group) if group.delimiter() == Delimiter::Brace => {
self.advance(); self.advance();
ast::Markup::Block(self.block(group.stream(), group.span())) ast::Markup::Block(self.block(group.stream(), SpanRange::single_span(group.span())))
}, },
// ??? // ???
token => { token => {
@ -193,7 +191,7 @@ impl Parser {
.unwrap_or_else(|_| abort!(lit, "expected string")); .unwrap_or_else(|_| abort!(lit, "expected string"));
ast::Markup::Literal { ast::Markup::Literal {
content, content,
span: lit.span(), span: SpanRange::single_span(lit.span()),
} }
} }
@ -210,18 +208,18 @@ impl Parser {
let body = loop { let body = loop {
match self.next() { match self.next() {
Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => { Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => {
break self.block(block.stream(), block.span()); break self.block(block.stream(), SpanRange::single_span(block.span()));
}, },
Some(token) => head.push(token), Some(token) => head.push(token),
None => { None => {
let head_span = ast::span_tokens(head); let mut span = ast::span_tokens(head);
let span = at_span.join(head_span).unwrap_or(head_span); span.first = at_span;
abort!(span, "expected body for this `@if`"); abort!(span, "expected body for this `@if`");
}, },
} }
}; };
segments.push(ast::Special { segments.push(ast::Special {
at_span, at_span: SpanRange::single_span(at_span),
head: head.into_iter().collect(), head: head.into_iter().collect(),
body, body,
}); });
@ -251,16 +249,15 @@ impl Parser {
_ => { _ => {
match self.next() { match self.next() {
Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Brace => { Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Brace => {
let body = self.block(group.stream(), group.span()); let body = self.block(group.stream(), SpanRange::single_span(group.span()));
segments.push(ast::Special { segments.push(ast::Special {
at_span, at_span: SpanRange::single_span(at_span),
head: vec![else_keyword].into_iter().collect(), head: vec![else_keyword].into_iter().collect(),
body, body,
}); });
}, },
_ => { _ => {
let else_span = else_keyword.span(); let span = SpanRange { first: at_span, last: else_keyword.span() };
let span = at_span.join(else_span).unwrap_or(else_span);
abort!(span, "expected body for this `@else`"); abort!(span, "expected body for this `@else`");
}, },
} }
@ -281,17 +278,17 @@ impl Parser {
let body = loop { let body = loop {
match self.next() { match self.next() {
Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => { Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => {
break self.block(block.stream(), block.span()); break self.block(block.stream(), SpanRange::single_span(block.span()));
}, },
Some(token) => head.push(token), Some(token) => head.push(token),
None => { None => {
let span = at_span.join(keyword_span).unwrap_or(keyword_span); let span = SpanRange { first: at_span, last: keyword_span };
abort!(span, "expected body for this `@while`"); abort!(span, "expected body for this `@while`");
}, },
} }
}; };
ast::Markup::Special { ast::Markup::Special {
segments: vec![ast::Special { at_span, head: head.into_iter().collect(), body }], segments: vec![ast::Special { at_span: SpanRange::single_span(at_span), head: head.into_iter().collect(), body }],
} }
} }
@ -309,7 +306,7 @@ impl Parser {
}, },
Some(token) => head.push(token), Some(token) => head.push(token),
None => { None => {
let span = at_span.join(keyword_span).unwrap_or(keyword_span); let span = SpanRange { first: at_span, last: keyword_span };
abort!(span, "missing `in` in `@for` loop"); abort!(span, "missing `in` in `@for` loop");
}, },
} }
@ -317,17 +314,17 @@ impl Parser {
let body = loop { let body = loop {
match self.next() { match self.next() {
Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => { Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => {
break self.block(block.stream(), block.span()); break self.block(block.stream(), SpanRange::single_span(block.span()));
}, },
Some(token) => head.push(token), Some(token) => head.push(token),
None => { None => {
let span = at_span.join(keyword_span).unwrap_or(keyword_span); let span = SpanRange { first: at_span, last: keyword_span };
abort!(span, "expected body for this `@for`"); abort!(span, "expected body for this `@for`");
}, },
} }
}; };
ast::Markup::Special { ast::Markup::Special {
segments: vec![ast::Special { at_span, head: head.into_iter().collect(), body }], segments: vec![ast::Special { at_span: SpanRange::single_span(at_span), head: head.into_iter().collect(), body }],
} }
} }
@ -340,17 +337,17 @@ impl Parser {
let (arms, arms_span) = loop { let (arms, arms_span) = loop {
match self.next() { match self.next() {
Some(TokenTree::Group(ref body)) if body.delimiter() == Delimiter::Brace => { Some(TokenTree::Group(ref body)) if body.delimiter() == Delimiter::Brace => {
let span = body.span(); let span = SpanRange::single_span(body.span());
break (self.with_input(body.stream()).match_arms(), span); break (self.with_input(body.stream()).match_arms(), span);
}, },
Some(token) => head.push(token), Some(token) => head.push(token),
None => { None => {
let span = at_span.join(keyword_span).unwrap_or(keyword_span); let span = SpanRange { first: at_span, last: keyword_span };
abort!(span, "expected body for this `@match`"); abort!(span, "expected body for this `@match`");
}, },
} }
}; };
ast::Markup::Match { at_span, head: head.into_iter().collect(), arms, arms_span } ast::Markup::Match { at_span: SpanRange::single_span(at_span), head: head.into_iter().collect(), arms, arms_span }
} }
fn match_arms(&mut self) -> Vec<ast::MatchArm> { fn match_arms(&mut self) -> Vec<ast::MatchArm> {
@ -389,7 +386,7 @@ impl Parser {
let body = match self.next() { let body = match self.next() {
// $pat => { $stmts } // $pat => { $stmts }
Some(TokenTree::Group(ref body)) if body.delimiter() == Delimiter::Brace => { Some(TokenTree::Group(ref body)) if body.delimiter() == Delimiter::Brace => {
let body = self.block(body.stream(), body.span()); let body = self.block(body.stream(), SpanRange::single_span(body.span()));
// Trailing commas are optional if the match arm is a braced block // Trailing commas are optional if the match arm is a braced block
if let Some(TokenTree::Punct(ref punct)) = self.peek() { if let Some(TokenTree::Punct(ref punct)) = self.peek() {
if punct.as_char() == ',' { if punct.as_char() == ',' {
@ -400,15 +397,13 @@ impl Parser {
}, },
// $pat => $expr // $pat => $expr
Some(first_token) => { Some(first_token) => {
let mut span = first_token.span(); let mut span = SpanRange::single_span(first_token.span());
let mut body = vec![first_token]; let mut body = vec![first_token];
loop { loop {
match self.next() { match self.next() {
Some(TokenTree::Punct(ref punct)) if punct.as_char() == ',' => break, Some(TokenTree::Punct(ref punct)) if punct.as_char() == ',' => break,
Some(token) => { Some(token) => {
if let Some(bigger_span) = span.join(token.span()) { span.last = token.span();
span = bigger_span;
}
body.push(token); body.push(token);
}, },
None => break, None => break,
@ -441,8 +436,8 @@ impl Parser {
} }
}, },
None => { None => {
let tokens_span = ast::span_tokens(tokens); let mut span = ast::span_tokens(tokens);
let span = at_span.join(tokens_span).unwrap_or(tokens_span); span.first = at_span;
abort!(span, "unexpected end of `@let` expression"); abort!(span, "unexpected end of `@let` expression");
} }
} }
@ -459,8 +454,8 @@ impl Parser {
} }
}, },
None => { None => {
let tokens_span = ast::span_tokens(tokens); let mut span = ast::span_tokens(tokens);
let span = at_span.join(tokens_span).unwrap_or(tokens_span); span.first = at_span;
abort!( abort!(
span, span,
"unexpected end of `@let` expression"; "unexpected end of `@let` expression";
@ -469,7 +464,7 @@ impl Parser {
}, },
} }
} }
ast::Markup::Let { at_span, tokens: tokens.into_iter().collect() } ast::Markup::Let { at_span: SpanRange::single_span(at_span), tokens: tokens.into_iter().collect() }
} }
/// Parses an element node. /// Parses an element node.
@ -486,7 +481,7 @@ impl Parser {
if punct.as_char() == ';' || punct.as_char() == '/' => { if punct.as_char() == ';' || punct.as_char() == '/' => {
// Void element // Void element
self.advance(); self.advance();
ast::ElementBody::Void { semi_span: punct.span() } ast::ElementBody::Void { semi_span: SpanRange::single_span(punct.span()) }
}, },
_ => { _ => {
match self.markup() { match self.markup() {
@ -546,20 +541,20 @@ impl Parser {
self.commit(attempt); self.commit(attempt);
let name = self.class_or_id_name(); let name = self.class_or_id_name();
let toggler = self.attr_toggler(); let toggler = self.attr_toggler();
attrs.push(ast::Attr::Class { dot_span: punct.span(), name, toggler }); attrs.push(ast::Attr::Class { dot_span: SpanRange::single_span(punct.span()), name, toggler });
}, },
// ID shorthand // ID shorthand
(None, Some(TokenTree::Punct(ref punct))) if punct.as_char() == '#' => { (None, Some(TokenTree::Punct(ref punct))) if punct.as_char() == '#' => {
self.commit(attempt); self.commit(attempt);
let name = self.class_or_id_name(); let name = self.class_or_id_name();
attrs.push(ast::Attr::Id { hash_span: punct.span(), name }); attrs.push(ast::Attr::Id { hash_span: SpanRange::single_span(punct.span()), name });
}, },
// If it's not a valid attribute, backtrack and bail out // If it's not a valid attribute, backtrack and bail out
_ => break, _ => break,
} }
} }
let mut attr_map: HashMap<String, Vec<Span>> = HashMap::new(); let mut attr_map: HashMap<String, Vec<SpanRange>> = HashMap::new();
let mut has_class = false; let mut has_class = false;
for attr in &attrs { for attr in &attrs {
let name = match attr { let name = match attr {
@ -607,7 +602,7 @@ impl Parser {
self.advance(); self.advance();
Some(ast::Toggler { Some(ast::Toggler {
cond: group.stream(), cond: group.stream(),
cond_span: group.span(), cond_span: SpanRange::single_span(group.span()),
}) })
}, },
_ => None, _ => None,
@ -657,7 +652,7 @@ impl Parser {
} }
/// Parses the given token stream as a Maud expression. /// Parses the given token stream as a Maud expression.
fn block(&mut self, body: TokenStream, outer_span: Span) -> ast::Block { fn block(&mut self, body: TokenStream, outer_span: SpanRange) -> ast::Block {
let markups = self.with_input(body).markups(); let markups = self.with_input(body).markups();
ast::Block { markups, outer_span } ast::Block { markups, outer_span }
} }