Repository: rinja-rs/askama (language: rust)

Issue:
Template compilation infinite loop / failed cycle detection
Hello,

I ran into the same bug as is reported is issue #515. That issue was closed as solved in 2021, with some cycle detection added in, but I have a simple case the cycle detection fails to trigger on.

The following project will reproduce it, running cargo build never halts.

`src/lib.rs`:
```rust
use askama::Template;

#[derive(Template)]
#[template(path = "page.html")]
pub struct Page;
```

`templates/base.html`:
```
{% extends base.html %}
```
`templates/page.html`:
```
{% extends base.html %}
```


Discussion / hints:
Is `page.html` necessary to trigger it?
Yes, if you do `path = "base.html"` in lib.rs the cycle detection triggers properly.
Interesting. Checking what's going on.

Fix patch:
diff --git a/askama_derive/src/input.rs b/askama_derive/src/input.rs
index 0c8bda2f1..4139ca87d 100644
--- a/askama_derive/src/input.rs
+++ b/askama_derive/src/input.rs
@@ -140,15 +140,12 @@ impl TemplateInput<'_> {
                         Extends(extends) if top => {
                             let extends = self.config.find_template(extends.path, Some(&path))?;
                             let dependency_path = (path.clone(), extends.clone());
-                            if dependency_graph.contains(&dependency_path) {
-                                return Err(format!(
-                                    "cyclic dependency in graph {:#?}",
-                                    dependency_graph
-                                        .iter()
-                                        .map(|e| format!("{:#?} --> {:#?}", e.0, e.1))
-                                        .collect::<Vec<String>>()
-                                )
-                                .into());
+                            if path == extends {
+                                // We add the path into the graph to have a better looking error.
+                                dependency_graph.push(dependency_path);
+                                return cyclic_graph_error(&dependency_graph);
+                            } else if dependency_graph.contains(&dependency_path) {
+                                return cyclic_graph_error(&dependency_graph);
                             }
                             dependency_graph.push(dependency_path);
                             add_to_check(extends)?;
@@ -420,6 +417,17 @@ const TEXT_TYPES: [(Mime, Mime); 7] = [
     (mime::IMAGE_SVG, mime::IMAGE_SVG),
 ];
 
+fn cyclic_graph_error(dependency_graph: &[(Rc<Path>, Rc<Path>)]) -> Result<(), CompileError> {
+    Err(format!(
+        "cyclic dependency in graph {:#?}",
+        dependency_graph
+            .iter()
+            .map(|e| format!("{:#?} --> {:#?}", e.0, e.1))
+            .collect::<Vec<String>>()
+    )
+    .into())
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;


Test patch:
diff --git a/testing/templates/cycle1.html b/testing/templates/cycle1.html
new file mode 100644
index 000000000..f48ce0cbd
--- /dev/null
+++ b/testing/templates/cycle1.html
@@ -0,0 +1,1 @@
+{% extends "cycle1.html" %}
diff --git a/testing/templates/cycle2.html b/testing/templates/cycle2.html
new file mode 100644
index 000000000..323560b98
--- /dev/null
+++ b/testing/templates/cycle2.html
@@ -0,0 +1,1 @@
+{% extends "cycle2.html" %}
diff --git a/testing/tests/ui/cycle.rs b/testing/tests/ui/cycle.rs
new file mode 100644
index 000000000..b6f7f6148
--- /dev/null
+++ b/testing/tests/ui/cycle.rs
@@ -0,0 +1,8 @@
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "cycle2.html")]
+struct Cycle;
+
+fn main() {
+}
diff --git a/testing/tests/ui/cycle.stderr b/testing/tests/ui/cycle.stderr
new file mode 100644
index 000000000..df765cefd
--- /dev/null
+++ b/testing/tests/ui/cycle.stderr
@@ -0,0 +1,9 @@
+error: cyclic dependency in graph [
+           "\"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle2.html/" --> \"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle2.html/"",
+       ]
+ --> tests/ui/cycle.rs:3:10
+  |
+3 | #[derive(Template)]
+  |          ^^^^^^^^
+  |
+  = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/testing/tests/ui/cycle2.rs b/testing/tests/ui/cycle2.rs
new file mode 100644
index 000000000..9fbae96ad
--- /dev/null
+++ b/testing/tests/ui/cycle2.rs
@@ -0,0 +1,8 @@
+use askama::Template;
+
+#[derive(Template)]
+#[template(path = "cycle1.html")]
+struct Cycle;
+
+fn main() {
+}
diff --git a/testing/tests/ui/cycle2.stderr b/testing/tests/ui/cycle2.stderr
new file mode 100644
index 000000000..682f85024
--- /dev/null
+++ b/testing/tests/ui/cycle2.stderr
@@ -0,0 +1,9 @@
+error: cyclic dependency in graph [
+           "\"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle1.html/" --> \"$WORKSPACE/target/tests/trybuild/askama_testing/templates/cycle1.html/"",
+       ]
+ --> tests/ui/cycle2.rs:3:10
+  |
+3 | #[derive(Template)]
+  |          ^^^^^^^^
+  |
+  = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)