mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-27 04:50:40 +00:00
Merge pull request #257 from Kijewski/pr-smaller-span
parser: shrink the size of `WithSpan` to one register
This commit is contained in:
commit
87f611e871
@ -10,8 +10,8 @@ use parser::node::{
|
||||
Whitespace, Ws,
|
||||
};
|
||||
use parser::{
|
||||
CharLit, CharPrefix, Expr, Filter, FloatKind, IntKind, Node, Num, StrLit, StrPrefix, Target,
|
||||
WithSpan,
|
||||
CharLit, CharPrefix, Expr, Filter, FloatKind, IntKind, Node, Num, Span, StrLit, StrPrefix,
|
||||
Target, WithSpan,
|
||||
};
|
||||
use rustc_hash::FxBuildHasher;
|
||||
|
||||
@ -263,7 +263,7 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
}
|
||||
Node::BlockDef(ref b) => {
|
||||
size_hint +=
|
||||
self.write_block(ctx, buf, Some(b.name), Ws(b.ws1.0, b.ws2.1), b)?;
|
||||
self.write_block(ctx, buf, Some(b.name), Ws(b.ws1.0, b.ws2.1), b.span())?;
|
||||
}
|
||||
Node::Include(ref i) => {
|
||||
size_hint += self.handle_include(ctx, buf, i)?;
|
||||
@ -276,9 +276,10 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
}
|
||||
Node::Macro(ref m) => {
|
||||
if level != AstLevel::Top {
|
||||
return Err(
|
||||
ctx.generate_error("macro blocks only allowed at the top level", m)
|
||||
);
|
||||
return Err(ctx.generate_error(
|
||||
"macro blocks only allowed at the top level",
|
||||
m.span(),
|
||||
));
|
||||
}
|
||||
self.flush_ws(m.ws1);
|
||||
self.prepare_ws(m.ws2);
|
||||
@ -290,17 +291,19 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
}
|
||||
Node::Import(ref i) => {
|
||||
if level != AstLevel::Top {
|
||||
return Err(
|
||||
ctx.generate_error("import blocks only allowed at the top level", i)
|
||||
);
|
||||
return Err(ctx.generate_error(
|
||||
"import blocks only allowed at the top level",
|
||||
i.span(),
|
||||
));
|
||||
}
|
||||
self.handle_ws(i.ws);
|
||||
}
|
||||
Node::Extends(ref e) => {
|
||||
if level != AstLevel::Top {
|
||||
return Err(
|
||||
ctx.generate_error("extend blocks only allowed at the top level", e)
|
||||
);
|
||||
return Err(ctx.generate_error(
|
||||
"extend blocks only allowed at the top level",
|
||||
e.span(),
|
||||
));
|
||||
}
|
||||
// No whitespace handling: child template top-level is not used,
|
||||
// except for the blocks defined in it.
|
||||
@ -746,26 +749,26 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
ref args,
|
||||
} = **call;
|
||||
if name == "super" {
|
||||
return self.write_block(ctx, buf, None, ws, call);
|
||||
return self.write_block(ctx, buf, None, ws, call.span());
|
||||
}
|
||||
|
||||
let (def, own_ctx) = if let Some(s) = scope {
|
||||
let path = ctx.imports.get(s).ok_or_else(|| {
|
||||
ctx.generate_error(format_args!("no import found for scope {s:?}"), call)
|
||||
ctx.generate_error(format_args!("no import found for scope {s:?}"), call.span())
|
||||
})?;
|
||||
let mctx = self.contexts.get(path).ok_or_else(|| {
|
||||
ctx.generate_error(format_args!("context for {path:?} not found"), call)
|
||||
ctx.generate_error(format_args!("context for {path:?} not found"), call.span())
|
||||
})?;
|
||||
let def = mctx.macros.get(name).ok_or_else(|| {
|
||||
ctx.generate_error(
|
||||
format_args!("macro {name:?} not found in scope {s:?}"),
|
||||
call,
|
||||
call.span(),
|
||||
)
|
||||
})?;
|
||||
(def, mctx)
|
||||
} else {
|
||||
let def = ctx.macros.get(name).ok_or_else(|| {
|
||||
ctx.generate_error(format_args!("macro {name:?} not found"), call)
|
||||
ctx.generate_error(format_args!("macro {name:?} not found"), call.span())
|
||||
})?;
|
||||
(def, ctx)
|
||||
};
|
||||
@ -790,7 +793,7 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
if !def.args.iter().any(|(arg, _)| arg == arg_name) {
|
||||
return Err(ctx.generate_error(
|
||||
format_args!("no argument named `{arg_name}` in macro {name:?}"),
|
||||
call,
|
||||
call.span(),
|
||||
));
|
||||
}
|
||||
named_arguments.insert(arg_name, (index, arg));
|
||||
@ -824,7 +827,7 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
"cannot have unnamed argument (`{arg}`) after named argument \
|
||||
in call to macro {name:?}"
|
||||
),
|
||||
call,
|
||||
call.span(),
|
||||
));
|
||||
}
|
||||
arg_expr
|
||||
@ -833,14 +836,14 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
let Expr::NamedArgument(name, _) = **arg_expr else { unreachable!() };
|
||||
return Err(ctx.generate_error(
|
||||
format_args!("`{name}` is passed more than once"),
|
||||
call,
|
||||
call.span(),
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
if let Some(default_value) = default_value {
|
||||
default_value
|
||||
} else {
|
||||
return Err(ctx.generate_error(format_args!("missing `{arg}` argument"), call));
|
||||
return Err(ctx.generate_error(format_args!("missing `{arg}` argument"), call.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -932,7 +935,7 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
&mut filter_buf,
|
||||
filter.filters.name,
|
||||
&filter.filters.arguments,
|
||||
filter,
|
||||
filter.span(),
|
||||
)?;
|
||||
let filter_buf = match display_wrap {
|
||||
DisplayWrap::Wrapped => filter_buf.into_string(),
|
||||
@ -961,7 +964,9 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
) -> Result<usize, CompileError> {
|
||||
self.flush_ws(i.ws);
|
||||
self.write_buf_writable(ctx, buf)?;
|
||||
let file_info = ctx.path.map(|path| FileInfo::of(i, path, ctx.parsed));
|
||||
let file_info = ctx
|
||||
.path
|
||||
.map(|path| FileInfo::of(i.span(), path, ctx.parsed));
|
||||
let path = self
|
||||
.input
|
||||
.config
|
||||
@ -1006,11 +1011,11 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(size_hint)
|
||||
}
|
||||
|
||||
fn is_shadowing_variable<T>(
|
||||
fn is_shadowing_variable(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
var: &Target<'a>,
|
||||
l: &WithSpan<'_, T>,
|
||||
l: Span<'_>,
|
||||
) -> Result<bool, CompileError> {
|
||||
match var {
|
||||
Target::Name(name) => {
|
||||
@ -1078,7 +1083,7 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
let mut expr_buf = Buffer::new();
|
||||
self.visit_expr(ctx, &mut expr_buf, val)?;
|
||||
|
||||
let shadowed = self.is_shadowing_variable(ctx, &l.var, l)?;
|
||||
let shadowed = self.is_shadowing_variable(ctx, &l.var, l.span())?;
|
||||
if shadowed {
|
||||
// Need to flush the buffer if the variable is being shadowed,
|
||||
// to ensure the old variable is used.
|
||||
@ -1104,13 +1109,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
// If `name` is `Some`, this is a call to a block definition, and we have to find
|
||||
// the first block for that name from the ancestry chain. If name is `None`, this
|
||||
// is from a `super()` call, and we can get the name from `self.super_block`.
|
||||
fn write_block<T>(
|
||||
fn write_block(
|
||||
&mut self,
|
||||
ctx: &Context<'a>,
|
||||
buf: &mut Buffer,
|
||||
name: Option<&'a str>,
|
||||
outer: Ws,
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<usize, CompileError> {
|
||||
if self.is_in_filter_block > 0 {
|
||||
return Err(ctx.generate_error("cannot have a block inside a filter block", node));
|
||||
@ -1384,7 +1389,7 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Expr::Filter(Filter {
|
||||
name,
|
||||
ref arguments,
|
||||
}) => self.visit_filter(ctx, buf, name, arguments, expr)?,
|
||||
}) => self.visit_filter(ctx, buf, name, arguments, expr.span())?,
|
||||
Expr::Unary(op, ref inner) => self.visit_unary(ctx, buf, op, inner)?,
|
||||
Expr::BinOp(op, ref left, ref right) => self.visit_binop(ctx, buf, op, left, right)?,
|
||||
Expr::Range(op, ref left, ref right) => {
|
||||
@ -1507,13 +1512,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
DisplayWrap::Unwrapped
|
||||
}
|
||||
|
||||
fn visit_filter<T>(
|
||||
fn visit_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
let filter = match name {
|
||||
"deref" => Self::_visit_deref_filter,
|
||||
@ -1534,13 +1539,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
filter(self, ctx, buf, name, args, node)
|
||||
}
|
||||
|
||||
fn _visit_custom_filter<T>(
|
||||
fn _visit_custom_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
_node: &WithSpan<'_, T>,
|
||||
_node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
buf.write(format_args!("filters::{name}("));
|
||||
self._visit_args(ctx, buf, args)?;
|
||||
@ -1548,13 +1553,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_builtin_filter<T>(
|
||||
fn _visit_builtin_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
_node: &WithSpan<'_, T>,
|
||||
_node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
buf.write(format_args!("rinja::filters::{name}("));
|
||||
self._visit_args(ctx, buf, args)?;
|
||||
@ -1562,13 +1567,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_urlencode<T>(
|
||||
fn _visit_urlencode(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
if cfg!(not(feature = "urlencode")) {
|
||||
return Err(ctx.generate_error(
|
||||
@ -1586,13 +1591,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_humansize<T>(
|
||||
fn _visit_humansize(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
_node: &WithSpan<'_, T>,
|
||||
_node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
// All filters return numbers, and any default formatted number is HTML safe.
|
||||
buf.write(format_args!(
|
||||
@ -1604,28 +1609,24 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_pluralize_filter<T>(
|
||||
fn _visit_pluralize_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
_name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
const SINGULAR: &WithSpan<'static, Expr<'static>> = &WithSpan::new(
|
||||
Expr::StrLit(StrLit {
|
||||
const SINGULAR: &WithSpan<'static, Expr<'static>> =
|
||||
&WithSpan::new_without_span(Expr::StrLit(StrLit {
|
||||
prefix: None,
|
||||
content: "",
|
||||
}),
|
||||
"",
|
||||
);
|
||||
const PLURAL: &WithSpan<'static, Expr<'static>> = &WithSpan::new(
|
||||
Expr::StrLit(StrLit {
|
||||
}));
|
||||
const PLURAL: &WithSpan<'static, Expr<'static>> =
|
||||
&WithSpan::new_without_span(Expr::StrLit(StrLit {
|
||||
prefix: None,
|
||||
content: "s",
|
||||
}),
|
||||
"",
|
||||
);
|
||||
}));
|
||||
|
||||
let (count, sg, pl) = match args {
|
||||
[count] => (count, SINGULAR, PLURAL),
|
||||
@ -1652,13 +1653,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Wrapped)
|
||||
}
|
||||
|
||||
fn _visit_linebreaks_filter<T>(
|
||||
fn _visit_linebreaks_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
if args.len() != 1 {
|
||||
return Err(ctx.generate_error(
|
||||
@ -1676,13 +1677,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_ref_filter<T>(
|
||||
fn _visit_ref_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
_name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
let arg = match args {
|
||||
[arg] => arg,
|
||||
@ -1693,13 +1694,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_deref_filter<T>(
|
||||
fn _visit_deref_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
_name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
let arg = match args {
|
||||
[arg] => arg,
|
||||
@ -1710,13 +1711,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_json_filter<T>(
|
||||
fn _visit_json_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
_name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
if cfg!(not(feature = "serde_json")) {
|
||||
return Err(ctx.generate_error(
|
||||
@ -1737,13 +1738,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
|
||||
fn _visit_safe_filter<T>(
|
||||
fn _visit_safe_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
_name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
if args.len() != 1 {
|
||||
return Err(ctx.generate_error("unexpected argument(s) in `safe` filter", node));
|
||||
@ -1754,13 +1755,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Wrapped)
|
||||
}
|
||||
|
||||
fn _visit_escape_filter<T>(
|
||||
fn _visit_escape_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
_name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
if args.len() > 2 {
|
||||
return Err(ctx.generate_error("only two arguments allowed to escape filter", node));
|
||||
@ -1777,7 +1778,7 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
format_args!(
|
||||
"invalid escaper `b{content:?}`. Expected a string, found a {kind}"
|
||||
),
|
||||
&args[1],
|
||||
args[1].span(),
|
||||
));
|
||||
}
|
||||
Some(content)
|
||||
@ -1815,13 +1816,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Ok(DisplayWrap::Wrapped)
|
||||
}
|
||||
|
||||
fn _visit_format_filter<T>(
|
||||
fn _visit_format_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
_name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
if !args.is_empty() {
|
||||
if let Expr::StrLit(ref fmt) = *args[0] {
|
||||
@ -1838,13 +1839,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
Err(ctx.generate_error(r#"use filter format like `"a={} b={}"|format(a, b)`"#, node))
|
||||
}
|
||||
|
||||
fn _visit_fmt_filter<T>(
|
||||
fn _visit_fmt_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
_name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
if let [_, arg2] = args {
|
||||
if let Expr::StrLit(ref fmt) = **arg2 {
|
||||
@ -1860,13 +1861,13 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
}
|
||||
|
||||
// Force type coercion on first argument to `join` filter (see #39).
|
||||
fn _visit_join_filter<T>(
|
||||
fn _visit_join_filter(
|
||||
&mut self,
|
||||
ctx: &Context<'_>,
|
||||
buf: &mut Buffer,
|
||||
_name: &str,
|
||||
args: &[WithSpan<'_, Expr<'_>>],
|
||||
_node: &WithSpan<'_, T>,
|
||||
_node: Span<'_>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
buf.write("rinja::filters::join((&");
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
@ -1986,7 +1987,7 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
buf.write("_loop_item.last");
|
||||
return Ok(DisplayWrap::Unwrapped);
|
||||
} else {
|
||||
return Err(ctx.generate_error("unknown loop variable", obj));
|
||||
return Err(ctx.generate_error("unknown loop variable", obj.span()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2022,9 +2023,10 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
"cycle" => match args {
|
||||
[arg] => {
|
||||
if matches!(**arg, Expr::Array(ref arr) if arr.is_empty()) {
|
||||
return Err(
|
||||
ctx.generate_error("loop.cycle(…) cannot use an empty array", arg)
|
||||
);
|
||||
return Err(ctx.generate_error(
|
||||
"loop.cycle(…) cannot use an empty array",
|
||||
arg.span(),
|
||||
));
|
||||
}
|
||||
buf.write(
|
||||
"\
|
||||
@ -2044,14 +2046,15 @@ impl<'a, 'h> Generator<'a, 'h> {
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
return Err(
|
||||
ctx.generate_error("loop.cycle(…) cannot use an empty array", left)
|
||||
);
|
||||
return Err(ctx.generate_error(
|
||||
"loop.cycle(…) cannot use an empty array",
|
||||
left.span(),
|
||||
));
|
||||
}
|
||||
},
|
||||
s => {
|
||||
return Err(
|
||||
ctx.generate_error(format_args!("unknown loop method: {s:?}"), left)
|
||||
ctx.generate_error(format_args!("unknown loop method: {s:?}"), left.span())
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -2421,7 +2424,7 @@ fn macro_call_ensure_arg_count(
|
||||
if expected_args != 1 { "s" } else { "" },
|
||||
call.args.len(),
|
||||
),
|
||||
call,
|
||||
call.span(),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use parser::node::{BlockDef, Macro};
|
||||
use parser::{Node, Parsed, WithSpan};
|
||||
use parser::{Node, Parsed, Span};
|
||||
use rustc_hash::FxBuildHasher;
|
||||
|
||||
use crate::config::Config;
|
||||
@ -80,29 +80,29 @@ impl Context<'_> {
|
||||
for n in nodes {
|
||||
match n {
|
||||
Node::Extends(e) => {
|
||||
ensure_top(top, e, path, parsed, "extends")?;
|
||||
ensure_top(top, e.span(), path, parsed, "extends")?;
|
||||
if extends.is_some() {
|
||||
return Err(CompileError::new(
|
||||
"multiple extend blocks found",
|
||||
Some(FileInfo::of(e, path, parsed)),
|
||||
Some(FileInfo::of(e.span(), path, parsed)),
|
||||
));
|
||||
}
|
||||
extends = Some(config.find_template(
|
||||
e.path,
|
||||
Some(path),
|
||||
Some(FileInfo::of(e, path, parsed)),
|
||||
Some(FileInfo::of(e.span(), path, parsed)),
|
||||
)?);
|
||||
}
|
||||
Node::Macro(m) => {
|
||||
ensure_top(top, m, path, parsed, "macro")?;
|
||||
ensure_top(top, m.span(), path, parsed, "macro")?;
|
||||
macros.insert(m.name, &**m);
|
||||
}
|
||||
Node::Import(import) => {
|
||||
ensure_top(top, import, path, parsed, "import")?;
|
||||
ensure_top(top, import.span(), path, parsed, "import")?;
|
||||
let path = config.find_template(
|
||||
import.path,
|
||||
Some(path),
|
||||
Some(FileInfo::of(import, path, parsed)),
|
||||
Some(FileInfo::of(import.span(), path, parsed)),
|
||||
)?;
|
||||
imports.insert(import.scope, path);
|
||||
}
|
||||
@ -141,11 +141,7 @@ impl Context<'_> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn generate_error<T>(
|
||||
&self,
|
||||
msg: impl fmt::Display,
|
||||
node: &WithSpan<'_, T>,
|
||||
) -> CompileError {
|
||||
pub(crate) fn generate_error(&self, msg: impl fmt::Display, node: Span<'_>) -> CompileError {
|
||||
CompileError::new(
|
||||
msg,
|
||||
self.path.map(|path| FileInfo::of(node, path, self.parsed)),
|
||||
@ -153,9 +149,9 @@ impl Context<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_top<T>(
|
||||
fn ensure_top(
|
||||
top: bool,
|
||||
node: &WithSpan<'_, T>,
|
||||
node: Span<'_>,
|
||||
path: &Path,
|
||||
parsed: &Parsed,
|
||||
kind: &str,
|
||||
|
@ -185,9 +185,14 @@ impl TemplateInput<'_> {
|
||||
// Add a dummy entry to `map` in order to prevent adding `path`
|
||||
// multiple times to `check`.
|
||||
let new_path = e.key();
|
||||
let source = parsed.source();
|
||||
let source = get_template_source(
|
||||
new_path,
|
||||
Some((&path, parsed.source(), n.span())),
|
||||
Some((
|
||||
&path,
|
||||
source,
|
||||
n.span().as_suffix_of(source).unwrap_or_default(),
|
||||
)),
|
||||
)?;
|
||||
check.push((new_path.clone(), source, Some(new_path.clone())));
|
||||
e.insert(Arc::default());
|
||||
@ -200,7 +205,7 @@ impl TemplateInput<'_> {
|
||||
let extends = self.config.find_template(
|
||||
extends.path,
|
||||
Some(&path),
|
||||
Some(FileInfo::of(extends, &path, &parsed)),
|
||||
Some(FileInfo::of(extends.span(), &path, &parsed)),
|
||||
)?;
|
||||
let dependency_path = (path.clone(), extends.clone());
|
||||
if path == extends {
|
||||
@ -220,7 +225,7 @@ impl TemplateInput<'_> {
|
||||
let import = self.config.find_template(
|
||||
import.path,
|
||||
Some(&path),
|
||||
Some(FileInfo::of(import, &path, &parsed)),
|
||||
Some(FileInfo::of(import.span(), &path, &parsed)),
|
||||
)?;
|
||||
add_to_check(import)?;
|
||||
}
|
||||
@ -231,7 +236,7 @@ impl TemplateInput<'_> {
|
||||
let include = self.config.find_template(
|
||||
include.path,
|
||||
Some(&path),
|
||||
Some(FileInfo::of(include, &path, &parsed)),
|
||||
Some(FileInfo::of(include.span(), &path, &parsed)),
|
||||
)?;
|
||||
add_to_check(include)?;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ use generator::template_to_string;
|
||||
use heritage::{Context, Heritage};
|
||||
use input::{Print, TemplateArgs, TemplateInput};
|
||||
use integration::Buffer;
|
||||
use parser::{Parsed, WithSpan, strip_common};
|
||||
use parser::{Parsed, strip_common};
|
||||
#[cfg(not(feature = "__standalone"))]
|
||||
use proc_macro::TokenStream as TokenStream12;
|
||||
#[cfg(feature = "__standalone")]
|
||||
@ -294,11 +294,12 @@ impl<'a> FileInfo<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn of<T>(node: &WithSpan<'a, T>, path: &'a Path, parsed: &'a Parsed) -> Self {
|
||||
fn of(node: parser::Span<'a>, path: &'a Path, parsed: &'a Parsed) -> Self {
|
||||
let source = parsed.source();
|
||||
Self {
|
||||
path,
|
||||
source: Some(parsed.source()),
|
||||
node_source: Some(node.span()),
|
||||
source: Some(source),
|
||||
node_source: node.as_suffix_of(source),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ use winnow::combinator::{
|
||||
use winnow::error::{ErrorKind, ParserError as _};
|
||||
|
||||
use crate::{
|
||||
CharLit, ErrorContext, Level, Num, ParseErr, ParseResult, PathOrIdentifier, StrLit, WithSpan,
|
||||
char_lit, filter, identifier, keyword, num_lit, path_or_identifier, skip_ws0, skip_ws1,
|
||||
str_lit, ws,
|
||||
CharLit, ErrorContext, Level, Num, ParseErr, ParseResult, PathOrIdentifier, Span, StrLit,
|
||||
WithSpan, char_lit, filter, identifier, keyword, num_lit, path_or_identifier, skip_ws0,
|
||||
skip_ws1, str_lit, ws,
|
||||
};
|
||||
|
||||
macro_rules! expr_prec_layer {
|
||||
@ -217,14 +217,14 @@ impl<'a> Expr<'a> {
|
||||
allow_underscore: bool,
|
||||
) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
let (_, level) = level.nest(i)?;
|
||||
let start = i;
|
||||
let start = Span::from(i);
|
||||
let range_right =
|
||||
move |i| (ws(alt(("..=", ".."))), opt(move |i| Self::or(i, level))).parse_next(i);
|
||||
let (i, expr) = alt((
|
||||
range_right.map(|(op, right)| {
|
||||
range_right.map(move |(op, right)| {
|
||||
WithSpan::new(Self::Range(op, None, right.map(Box::new)), start)
|
||||
}),
|
||||
(move |i| Self::or(i, level), opt(range_right)).map(|(left, right)| match right {
|
||||
(move |i| Self::or(i, level), opt(range_right)).map(move |(left, right)| match right {
|
||||
Some((op, right)) => WithSpan::new(
|
||||
Self::Range(op, Some(Box::new(left)), right.map(Box::new)),
|
||||
start,
|
||||
|
@ -114,11 +114,11 @@ impl<'a> Ast<'a> {
|
||||
Ok(("", nodes)) => Ok(Self { nodes }),
|
||||
Ok(_) | Err(winnow::error::ErrMode::Incomplete(_)) => unreachable!(),
|
||||
Err(
|
||||
winnow::error::ErrMode::Backtrack(ErrorContext { input, message, .. })
|
||||
| winnow::error::ErrMode::Cut(ErrorContext { input, message, .. }),
|
||||
winnow::error::ErrMode::Backtrack(ErrorContext { span, message, .. })
|
||||
| winnow::error::ErrMode::Cut(ErrorContext { span, message, .. }),
|
||||
) => Err(ParseError {
|
||||
message,
|
||||
offset: src.len() - input.len(),
|
||||
offset: span.offset_from(src).unwrap_or_default(),
|
||||
file_path,
|
||||
}),
|
||||
}
|
||||
@ -134,19 +134,76 @@ impl<'a> Ast<'a> {
|
||||
/// in the code generation.
|
||||
pub struct WithSpan<'a, T> {
|
||||
inner: T,
|
||||
span: &'a str,
|
||||
span: Span<'a>,
|
||||
}
|
||||
|
||||
/// An location in `&'a str`
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Span<'a>(&'a [u8; 0]);
|
||||
|
||||
impl Default for Span<'static> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Span<'a> {
|
||||
#[inline]
|
||||
pub const fn empty() -> Self {
|
||||
Self(&[])
|
||||
}
|
||||
|
||||
pub fn offset_from(self, start: &'a str) -> Option<usize> {
|
||||
let start_range = start.as_bytes().as_ptr_range();
|
||||
let this_ptr = self.0.as_slice().as_ptr();
|
||||
match start_range.contains(&this_ptr) {
|
||||
// SAFETY: we just checked that `this_ptr` is inside `start_range`
|
||||
true => Some(unsafe { this_ptr.offset_from(start_range.start) as usize }),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_suffix_of(self, start: &'a str) -> Option<&'a str> {
|
||||
let offset = self.offset_from(start)?;
|
||||
match start.is_char_boundary(offset) {
|
||||
true => Some(&start[offset..]),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Span<'a> {
|
||||
#[inline]
|
||||
fn from(value: &'a str) -> Self {
|
||||
Self(value[..0].as_bytes().try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> WithSpan<'a, T> {
|
||||
pub const fn new(inner: T, span: &'a str) -> Self {
|
||||
Self { inner, span }
|
||||
#[inline]
|
||||
pub fn new(inner: T, span: impl Into<Span<'a>>) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
span: span.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> &'a str {
|
||||
#[inline]
|
||||
pub const fn new_without_span(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
span: Span::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn span(&self) -> Span<'a> {
|
||||
self.span
|
||||
}
|
||||
|
||||
pub fn deconstruct(self) -> (T, &'a str) {
|
||||
#[inline]
|
||||
pub fn deconstruct(self) -> (T, Span<'a>) {
|
||||
let Self { inner, span } = self;
|
||||
(inner, span)
|
||||
}
|
||||
@ -229,18 +286,18 @@ pub(crate) type ParseResult<'a, T = &'a str> = Result<(&'a str, T), ParseErr<'a>
|
||||
/// `rinja`'s users experience less good (since this generic is only needed for `nom`).
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ErrorContext<'a> {
|
||||
pub(crate) input: &'a str,
|
||||
pub(crate) span: Span<'a>,
|
||||
pub(crate) message: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
impl<'a> ErrorContext<'a> {
|
||||
fn unclosed(kind: &str, tag: &str, i: &'a str) -> Self {
|
||||
Self::new(format!("unclosed {kind}, missing {tag:?}"), i)
|
||||
fn unclosed(kind: &str, tag: &str, span: impl Into<Span<'a>>) -> Self {
|
||||
Self::new(format!("unclosed {kind}, missing {tag:?}"), span)
|
||||
}
|
||||
|
||||
fn new(message: impl Into<Cow<'static, str>>, input: &'a str) -> Self {
|
||||
fn new(message: impl Into<Cow<'static, str>>, span: impl Into<Span<'a>>) -> Self {
|
||||
Self {
|
||||
input,
|
||||
span: span.into(),
|
||||
message: Some(message.into()),
|
||||
}
|
||||
}
|
||||
@ -249,7 +306,7 @@ impl<'a> ErrorContext<'a> {
|
||||
impl<'a> winnow::error::ParserError<&'a str> for ErrorContext<'a> {
|
||||
fn from_error_kind(input: &'a str, _code: ErrorKind) -> Self {
|
||||
Self {
|
||||
input,
|
||||
span: input.into(),
|
||||
message: None,
|
||||
}
|
||||
}
|
||||
@ -262,7 +319,7 @@ impl<'a> winnow::error::ParserError<&'a str> for ErrorContext<'a> {
|
||||
impl<'a, E: std::fmt::Display> FromExternalError<&'a str, E> for ErrorContext<'a> {
|
||||
fn from_external_error(input: &'a str, _kind: ErrorKind, e: E) -> Self {
|
||||
Self {
|
||||
input,
|
||||
span: input.into(),
|
||||
message: Some(Cow::Owned(e.to_string())),
|
||||
}
|
||||
}
|
||||
@ -1213,3 +1270,11 @@ mod test {
|
||||
assert!(str_lit.parse_next(r#"d"hello""#).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_span_size() {
|
||||
assert_eq!(
|
||||
std::mem::size_of::<Span<'static>>(),
|
||||
std::mem::size_of::<*const ()>()
|
||||
);
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ use winnow::token::{any, tag};
|
||||
|
||||
use crate::memchr_splitter::{Splitter1, Splitter2, Splitter3};
|
||||
use crate::{
|
||||
ErrorContext, Expr, Filter, ParseResult, State, Target, WithSpan, filter, identifier, keyword,
|
||||
skip_till, skip_ws0, str_lit_without_prefix, ws,
|
||||
ErrorContext, Expr, Filter, ParseResult, Span, State, Target, WithSpan, filter, identifier,
|
||||
keyword, skip_till, skip_ws0, str_lit_without_prefix, ws,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -43,7 +43,9 @@ impl<'a> Node<'a> {
|
||||
&err
|
||||
{
|
||||
if err.message.is_none() {
|
||||
opt(|i| unexpected_tag(i, s)).parse_next(err.input)?;
|
||||
if let Some(span) = err.span.as_suffix_of(i) {
|
||||
opt(|i| unexpected_tag(i, s)).parse_next(span)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Err(err);
|
||||
@ -184,7 +186,7 @@ impl<'a> Node<'a> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn span(&self) -> &str {
|
||||
pub fn span(&self) -> Span<'a> {
|
||||
match self {
|
||||
Self::Lit(span) => span.span,
|
||||
Self::Comment(span) => span.span,
|
||||
@ -218,7 +220,9 @@ fn cut_node<'a, O>(
|
||||
&result
|
||||
{
|
||||
if err.message.is_none() {
|
||||
opt(|i| unexpected_raw_tag(kind, i)).parse_next(err.input)?;
|
||||
if let Some(span) = err.span.as_suffix_of(i) {
|
||||
opt(|i| unexpected_raw_tag(kind, i)).parse_next(span)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
|
@ -1,9 +1,14 @@
|
||||
use crate::node::{Lit, Whitespace, Ws};
|
||||
use crate::{Ast, Expr, Filter, InnerSyntax, Node, Num, StrLit, Syntax, SyntaxBuilder, WithSpan};
|
||||
use crate::{
|
||||
Ast, Expr, Filter, InnerSyntax, Node, Num, Span, StrLit, Syntax, SyntaxBuilder, WithSpan,
|
||||
};
|
||||
|
||||
impl<T> WithSpan<'static, T> {
|
||||
fn no_span(inner: T) -> Self {
|
||||
Self { inner, span: "" }
|
||||
Self {
|
||||
inner,
|
||||
span: Span::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1122,3 +1127,12 @@ fn extends_with_whitespace_control() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fuzzed_span_is_not_substring_of_source() {
|
||||
let _: Result<Ast<'_>, crate::ParseError> = Ast::from_str(
|
||||
include_str!("../tests/fuzzed_span_is_not_substring_of_source.bin"),
|
||||
None,
|
||||
&Syntax::default(),
|
||||
);
|
||||
}
|
||||
|
BIN
rinja_parser/tests/fuzzed_span_is_not_substring_of_source.bin
Normal file
BIN
rinja_parser/tests/fuzzed_span_is_not_substring_of_source.bin
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user