From 8745bcf99b8fcf4da70d26bb88580a107dff3bd6 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume.gomez@huawei.com>
Date: Thu, 19 Sep 2024 23:46:42 +0200
Subject: [PATCH] Add tidy check for rustdoc templates to ensure the whitespace
 characters are all stripped

---
 src/tools/tidy/src/lib.rs               |  1 +
 src/tools/tidy/src/main.rs              |  1 +
 src/tools/tidy/src/rustdoc_templates.rs | 58 +++++++++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 src/tools/tidy/src/rustdoc_templates.rs

diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index 3215896091dde..1e7eb82b83e98 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -82,6 +82,7 @@ pub mod pal;
 pub mod run_make_tests;
 pub mod rustdoc_css_themes;
 pub mod rustdoc_gui_tests;
+pub mod rustdoc_templates;
 pub mod style;
 pub mod target_policy;
 pub mod target_specific_tests;
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index e6d21bfb245f7..38b3f3f6417a9 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -108,6 +108,7 @@ fn main() {
         check!(mir_opt_tests, &tests_path, bless);
         check!(rustdoc_gui_tests, &tests_path);
         check!(rustdoc_css_themes, &librustdoc_path);
+        check!(rustdoc_templates, &librustdoc_path);
         check!(known_bug, &crashes_path);
         check!(unknown_revision, &tests_path);
 
diff --git a/src/tools/tidy/src/rustdoc_templates.rs b/src/tools/tidy/src/rustdoc_templates.rs
new file mode 100644
index 0000000000000..6c8530e6366f6
--- /dev/null
+++ b/src/tools/tidy/src/rustdoc_templates.rs
@@ -0,0 +1,58 @@
+//! Tidy check to ensure that rustdoc templates didn't forget a `{# #}` to strip extra whitespace
+//! characters.
+
+use std::ffi::OsStr;
+use std::path::Path;
+
+use ignore::DirEntry;
+
+use crate::walk::walk;
+
+// Array containing `("beginning of tag", "end of tag")`.
+const TAGS: &[(&str, &str)] = &[("{#", "#}"), ("{%", "%}"), ("{{", "}}")];
+
+pub fn check(librustdoc_path: &Path, bad: &mut bool) {
+    walk(
+        &librustdoc_path.join("html/templates"),
+        |path, is_dir| is_dir || !path.extension().is_some_and(|ext| ext == OsStr::new("html")),
+        &mut |path: &DirEntry, file_content: &str| {
+            let mut lines = file_content.lines().enumerate().peekable();
+
+            while let Some((pos, line)) = lines.next() {
+                let line = line.trim();
+                if TAGS.iter().any(|(_, tag)| line.ends_with(tag)) {
+                    continue;
+                }
+                let Some(next_line) = lines.peek().map(|(_, next_line)| next_line.trim()) else {
+                    continue;
+                };
+                if TAGS.iter().any(|(tag, _)| next_line.starts_with(tag)) {
+                    continue;
+                }
+                // Maybe this is a multi-line tag, let's filter it out then.
+                match TAGS.iter().find_map(|(tag, end_tag)| {
+                    if line.rfind(tag).is_some() { Some(end_tag) } else { None }
+                }) {
+                    None => {
+                        // No it's not, let's error.
+                        tidy_error!(
+                            bad,
+                            "`{}` at line {}: missing `{{# #}}` at the end of the line",
+                            path.path().display(),
+                            pos + 1,
+                        );
+                    }
+                    Some(end_tag) => {
+                        // We skip the tag.
+                        while let Some((_, next_line)) = lines.peek() {
+                            if next_line.contains(end_tag) {
+                                break;
+                            }
+                            lines.next();
+                        }
+                    }
+                }
+            }
+        },
+    );
+}