-
-
Notifications
You must be signed in to change notification settings - Fork 224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better (post-parser) errors #1058
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the end result too, but there is a lot in the diff I don't like. Please be careful about minimizing the necessary changes and make some smaller commits.
askama_derive/src/input.rs
Outdated
@@ -55,18 +55,20 @@ impl TemplateInput<'_> { | |||
PathBuf::from(format!("{}.{}", ast.ident, ext)).into() | |||
} | |||
(&Source::Source(_), None) => { | |||
return Err("must include 'ext' attribute when using 'source' attribute".into()) | |||
return Err(CompileError::new( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to me like none of the call sites that pass None
as the second argument for CompileError::new()
actually need to change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree. I removed the From
implementations on purpose to ensure that we don't use .into()
by mistake in the future. It's less convenient but it forces to think about whether or not you can provide context on this error or not. If not, then you use None
and it's done.
Side effect: it also allows us (reviewers) to spot it more easily when reviewing and to check if it could in fact provide context to the error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change should definitely not be in the same commit as all these other changes, because it is a noop change which makes the diff very verbose and thus hard to review. We can reintroduce it in a later commit, although I would suggest it might make more sense to attach error()
methods to contexts like TemplateInput
that can refer to the available context directly.
askama_parser/src/node.rs
Outdated
pub struct Comment<'a> { | ||
pub ws: Ws, | ||
pub content: &'a str, | ||
} | ||
|
||
// We never want to compare with `content`. | ||
impl<'a> PartialEq for Comment<'a> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be in a separate commit.
askama_parser/src/lib.rs
Outdated
@@ -155,6 +136,54 @@ impl fmt::Display for ParseError { | |||
pub(crate) type ParseErr<'a> = nom::Err<ErrorContext<'a>>; | |||
pub(crate) type ParseResult<'a, T = &'a str> = Result<(&'a str, T), ParseErr<'a>>; | |||
|
|||
pub fn generate_row_and_column(src: &str, input: &str) -> (usize, usize, String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extracting these from Ast::from_str()
should be a separate commit.
Three or four element tuples should be structs with named fields.
askama_parser/src/expr.rs
Outdated
Some(Self::Call(args)) => expr = Expr::Call(expr.into(), args), | ||
Some(Self::Try) => expr = Expr::Try(expr.into()), | ||
Some(Self::Attr(attr)) => { | ||
expr = Expr { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest you add a constructor on Expr
to minimize the syntactic overhead of the wrapper here.
askama_parser/src/expr.rs
Outdated
/// This variant should never be used directly. It is created when generating filter blocks. | ||
Generated(String), | ||
#[derive(Clone, Debug)] | ||
pub struct Expr<'a> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to minimize the diff here, use a wrapper for the existing Expr
type instead of this renaming. Suggest you might be able to reuse the same wrapper for both Node
and Expr
, it could potentially implement Deref
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you name this new wrapper type? ExprOrNode
?
EDIT: Which would look like:
enum ExprOrNode<'a> {
Expr(&'a str, Expr<'a>),
Node(&'a str, Node<'a>),
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would force Node::Expr
to use Box
to prevent infinite type size too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More like:
struct WithSpan<'a, T> {
inner: T,
span: &'a str,
}
impl Deref for WithSpan<'a> {
type Target = T;
..
}
Might even be possible to use this to replace the ugly TemplateContents
trait?
@@ -127,13 +132,16 @@ impl TemplateInput<'_> { | |||
let mut nested = vec![parsed.nodes()]; | |||
while let Some(nodes) = nested.pop() { | |||
for n in nodes { | |||
let mut add_to_check = |path: Rc<Path>| -> Result<(), CompileError> { | |||
if !map.contains_key(&path) { | |||
let mut add_to_check = |new_path: Rc<Path>| -> Result<(), CompileError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please avoid/isolate unrelated changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not unrelated, I use the captured parent path
, but to do so I needed to rename the closure argument. I use it here:
let source = get_template_source(
&new_path,
Some((&path, parsed.source(), n.template_content())),
)?;
askama_derive/src/heritage.rs
Outdated
} | ||
|
||
impl Context<'_> { | ||
pub(crate) fn new_empty(parsed: &Parsed) -> Context<'_> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's call this empty()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
ced5d7d
to
9ca3190
Compare
864cfd3
to
22d352b
Compare
Finally reworked the whole git history and made the suggested changes. |
@@ -155,6 +129,72 @@ impl fmt::Display for ParseError { | |||
pub(crate) type ParseErr<'a> = nom::Err<ErrorContext<'a>>; | |||
pub(crate) type ParseResult<'a, T = &'a str> = Result<(&'a str, T), ParseErr<'a>>; | |||
|
|||
pub struct ErrorInfo { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although the direction is good, this still has too much going on. Suggest a few steps, maybe in a separate PR?
- Extract all of this code from
Ast::from_str()
into aParseError::new()
function - Unify the row/column display to
{row}:{column}
for both path and non-path errors - Extract an
ErrorInfo
type with animpl Display
that takes care of the final formatting step, which will need to include apath: Option<Rc<Path>>
- To the extent that you need what is here
generate_row_and_column()
andgenerate_error_info()
later in the PR, they should perhaps be constructors onErrorInfo
.
Also please keep functions in top-down order -- maybe do more self-review to check for style issues? Reviewing your changes is taking many round trips.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feedback is appreciated. I don't plan to send another PR for this, all changes are tied together and at this point your suggestions (even if great) are only improvements over this code. I'll implement what you suggested though.
Also please keep functions in top-down order -- maybe do more self-review to check for style issues? Reviewing your changes is taking many round trips.
Well, as you said, it's code styling, so we're entering personal preferences. I don't mind following a project code's style but if there is no test to enforce it and tell me what and where I did something wrong, then you shouldn't expect people to remember it or to implement it as such. Therefore, I don't plan to try to remember what style you want to apply until there is a check for it, I work on way too many different codebases to have motivation for this. If this is a blocker for you, then I'll just fork askama from this point and implement what I need on my side while backporting changes from your repository to mine. Just to be clear: this is not a hostile take or anything, I really love askama
and really enjoy your suggestions. It's just that you have high expectations for PRs, and I don't have the tools nor the time to live up to your expectations. So I'll let it up to you. I don't mind if you send commits to my PRs to fix code formatting, this is very welcome as well.
I'll try to update this PR in the next days since #1061 got merged. Still didn't applied your comments too, just came back from a hackfest. |
Fixes https://github.com/djc/askama/issues/926.
This is very big because a lot of small changes everywhere. However, the result is really great (at least for me 😺 ). Take a look at the updated ui tests to see the impact of this PR.