tidy/
rustdoc_templates.rs

1//! Tidy check to ensure that rustdoc templates didn't forget a `{# #}` to strip extra whitespace
2//! characters.
3
4use std::ffi::OsStr;
5use std::path::Path;
6
7use ignore::DirEntry;
8
9use crate::walk::walk;
10
11// Array containing `("beginning of tag", "end of tag")`.
12const TAGS: &[(&str, &str)] = &[("{#", "#}"), ("{%", "%}"), ("{{", "}}")];
13
14pub fn check(librustdoc_path: &Path, bad: &mut bool) {
15    walk(
16        &librustdoc_path.join("html/templates"),
17        |path, is_dir| is_dir || !path.extension().is_some_and(|ext| ext == OsStr::new("html")),
18        &mut |path: &DirEntry, file_content: &str| {
19            let mut lines = file_content.lines().enumerate().peekable();
20
21            while let Some((pos, line)) = lines.next() {
22                let line = line.trim();
23                if let Some(need_next_line_check) = TAGS.iter().find_map(|(tag, end_tag)| {
24                    // We first check if the line ends with a jinja tag.
25                    if !line.ends_with(end_tag) {
26                        return None;
27                    // Then we check if this a comment tag.
28                    } else if *tag != "{#" {
29                        return Some(false);
30                    // And finally we check if the comment is empty (ie, only there to strip
31                    // extra whitespace characters).
32                    } else if let Some(start_pos) = line.rfind(tag) {
33                        Some(line[start_pos + 2..].trim() == "#}")
34                    } else {
35                        Some(false)
36                    }
37                }) {
38                    // All good, the line is ending is a jinja tag. But maybe this tag is useless
39                    // if the next line starts with a jinja tag as well!
40                    //
41                    // However, only (empty) comment jinja tags are concerned about it.
42                    if need_next_line_check
43                        && lines.peek().is_some_and(|(_, next_line)| {
44                            let next_line = next_line.trim_start();
45                            TAGS.iter().any(|(tag, _)| next_line.starts_with(tag))
46                        })
47                    {
48                        // It seems like ending this line with a jinja tag is not needed after all.
49                        tidy_error!(
50                            bad,
51                            "`{}` at line {}: unneeded `{{# #}}` tag at the end of the line",
52                            path.path().display(),
53                            pos + 1,
54                        );
55                    }
56                    continue;
57                }
58                let Some(next_line) = lines.peek().map(|(_, next_line)| next_line.trim()) else {
59                    continue;
60                };
61                if TAGS.iter().any(|(tag, _)| next_line.starts_with(tag)) {
62                    continue;
63                }
64                // Maybe this is a multi-line tag, let's filter it out then.
65                match TAGS.iter().find_map(|(tag, end_tag)| {
66                    if line.rfind(tag).is_some() { Some(end_tag) } else { None }
67                }) {
68                    None => {
69                        // No it's not, let's error.
70                        tidy_error!(
71                            bad,
72                            "`{}` at line {}: missing `{{# #}}` at the end of the line",
73                            path.path().display(),
74                            pos + 1,
75                        );
76                    }
77                    Some(end_tag) => {
78                        // We skip the tag.
79                        while let Some((_, next_line)) = lines.peek() {
80                            if next_line.contains(end_tag) {
81                                break;
82                            }
83                            lines.next();
84                        }
85                    }
86                }
87            }
88        },
89    );
90}