Skip to content

Commit ff311cb

Browse files
committed
[wip]
1 parent 7c4ac06 commit ff311cb

File tree

18 files changed

+546
-12
lines changed

18 files changed

+546
-12
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,6 +1721,16 @@ dependencies = [
17211721
"syn 1.0.109",
17221722
]
17231723

1724+
[[package]]
1725+
name = "htmldocck"
1726+
version = "0.1.0"
1727+
dependencies = [
1728+
"getopts",
1729+
"regex",
1730+
"shlex",
1731+
"unicode-width",
1732+
]
1733+
17241734
[[package]]
17251735
name = "http"
17261736
version = "0.2.12"

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ members = [
3131
"src/tools/miri/cargo-miri",
3232
"src/tools/rustdoc-themes",
3333
"src/tools/unicode-table-generator",
34+
"src/tools/htmldocck",
3435
"src/tools/jsondocck",
3536
"src/tools/jsondoclint",
3637
"src/tools/llvm-bitcode-linker",

src/bootstrap/src/core/build_steps/clippy.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,8 @@ lint_any!(
311311
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
312312
Compiletest, "src/tools/compiletest", "compiletest";
313313
CoverageDump, "src/tools/coverage-dump", "coverage-dump";
314-
Jsondocck, "src/tools/jsondocck", "jsondocck";
314+
HtmldocCk, "src/tools/htmldocck", "htmldocck";
315+
JsondocCk, "src/tools/jsondocck", "jsondocck";
315316
Jsondoclint, "src/tools/jsondoclint", "jsondoclint";
316317
LintDocs, "src/tools/lint-docs", "lint-docs";
317318
LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker";

src/bootstrap/src/core/build_steps/test.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,13 +1766,18 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
17661766
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler));
17671767
}
17681768

1769+
if mode == "rustdoc" {
1770+
// Use the beta compiler for htmldocck.
1771+
let compiler = compiler.with_stage(0);
1772+
cmd.arg("--htmldocck-path").arg(builder.ensure(tool::HtmlDocCk { compiler, target }));
1773+
}
1774+
17691775
if mode == "rustdoc-json" {
1770-
// Use the beta compiler for jsondocck
1771-
let json_compiler = compiler.with_stage(0);
1772-
cmd.arg("--jsondocck-path")
1773-
.arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target }));
1776+
// Use the beta compiler for jsondocck.
1777+
let compiler = compiler.with_stage(0);
1778+
cmd.arg("--jsondocck-path").arg(builder.ensure(tool::JsonDocCk { compiler, target }));
17741779
cmd.arg("--jsondoclint-path")
1775-
.arg(builder.ensure(tool::JsonDocLint { compiler: json_compiler, target }));
1780+
.arg(builder.ensure(tool::JsonDocLint { compiler, target }));
17761781
}
17771782

17781783
if mode == "coverage-map" {

src/bootstrap/src/core/build_steps/tool.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ bootstrap_tool!(
303303
RustInstaller, "src/tools/rust-installer", "rust-installer";
304304
RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes";
305305
LintDocs, "src/tools/lint-docs", "lint-docs";
306+
HtmlDocCk, "src/tools/htmldocck", "htmldocck";
306307
JsonDocCk, "src/tools/jsondocck", "jsondocck";
307308
JsonDocLint, "src/tools/jsondoclint", "jsondoclint";
308309
HtmlChecker, "src/tools/html-checker", "html-checker";

src/bootstrap/src/core/builder.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,8 @@ impl<'a> Builder<'a> {
758758
clippy::CollectLicenseMetadata,
759759
clippy::Compiletest,
760760
clippy::CoverageDump,
761-
clippy::Jsondocck,
761+
clippy::HtmldocCk,
762+
clippy::JsondocCk,
762763
clippy::Jsondoclint,
763764
clippy::LintDocs,
764765
clippy::LlvmBitcodeLinker,

src/tools/compiletest/src/common.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,12 @@ pub struct Config {
193193
/// The coverage-dump executable.
194194
pub coverage_dump_path: Option<PathBuf>,
195195

196-
/// The Python executable to use for LLDB and htmldocck.
196+
/// The Python executable to use for LLDB.
197197
pub python: String,
198198

199+
/// The htmldocck executable.
200+
pub htmldocck_path: Option<String>,
201+
199202
/// The jsondocck executable.
200203
pub jsondocck_path: Option<String>,
201204

src/tools/compiletest/src/header/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ impl ConfigBuilder {
131131
"--compile-lib-path=",
132132
"--run-lib-path=",
133133
"--python=",
134+
// FIXME(fmease): Do we need to set htmldocck-path to "", too?
134135
"--jsondocck-path=",
135136
"--src-base=",
136137
"--build-base=",

src/tools/compiletest/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
4848
.optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH")
4949
.optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH")
5050
.optopt("", "coverage-dump-path", "path to coverage-dump to use in tests", "PATH")
51+
// FIXME(fmease): fix docs here
5152
.reqopt("", "python", "path to python to use for doc tests", "PATH")
53+
.optopt("", "htmldocck-path", "path to htmldocck to use for doc tests", "PATH")
5254
.optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH")
5355
.optopt("", "jsondoclint-path", "path to jsondoclint to use for doc tests", "PATH")
5456
.optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
@@ -235,6 +237,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
235237
rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from),
236238
coverage_dump_path: matches.opt_str("coverage-dump-path").map(PathBuf::from),
237239
python: matches.opt_str("python").unwrap(),
240+
htmldocck_path: matches.opt_str("htmldocck-path"),
238241
jsondocck_path: matches.opt_str("jsondocck-path"),
239242
jsondoclint_path: matches.opt_str("jsondoclint-path"),
240243
valgrind_path: matches.opt_str("valgrind-path"),
@@ -617,6 +620,7 @@ fn common_inputs_stamp(config: &Config) -> Stamp {
617620

618621
if let Some(ref rustdoc_path) = config.rustdoc_path {
619622
stamp.add_path(&rustdoc_path);
623+
// FIXME(fmease): Update to `src/tools/htmldocck` or rm entirely
620624
stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
621625
}
622626

src/tools/compiletest/src/runtest.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3024,9 +3024,16 @@ impl<'test> TestCx<'test> {
30243024
if self.props.check_test_line_numbers_match {
30253025
self.check_rustdoc_test_option(proc_res);
30263026
} else {
3027-
let root = self.config.find_rust_src_root().unwrap();
3028-
let mut cmd = Command::new(&self.config.python);
3029-
cmd.arg(root.join("src/etc/htmldocck.py")).arg(&out_dir).arg(&self.testpaths.file);
3027+
// FIXME(fmease): Temporary commented out code:
3028+
// FIXME(fmease): I don't like this unwrap!
3029+
let mut cmd = Command::new(self.config.htmldocck_path.as_ref().unwrap());
3030+
cmd.arg("--doc-dir").arg(&out_dir).arg("--template").arg(&self.testpaths.file);
3031+
3032+
// let root = self.config.find_rust_src_root().unwrap();
3033+
// let mut cmd = Command::new(&self.config.python);
3034+
// cmd.arg(root.join("src/etc/htmldocck.py"));
3035+
// cmd.arg(&out_dir).arg(&self.testpaths.file);
3036+
30303037
if self.config.bless {
30313038
cmd.arg("--bless");
30323039
}

src/tools/htmldocck/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "htmldocck"
3+
version = "0.1.0"
4+
description = "A test framework for rustdoc's HTML backend"
5+
edition = "2021"
6+
7+
[dependencies]
8+
getopts = "0.2"
9+
regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace
10+
shlex = "1.3.0"
11+
unicode-width = "0.1.4"

src/tools/htmldocck/src/cache.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use std::{collections::HashMap, path::Path};
2+
3+
pub(crate) struct Cache<'a> {
4+
root: &'a Path,
5+
// FIXME: `&'a str`s
6+
files: HashMap<String, String>,
7+
// FIXME: `&'a str`, comment what this is for -- `-`
8+
last_path: Option<String>,
9+
}
10+
11+
impl<'a> Cache<'a> {
12+
pub(crate) fn new(root: &'a Path) -> Self {
13+
Self { root, files: HashMap::new(), last_path: None }
14+
}
15+
16+
pub(crate) fn read_file(&mut self, path: String) -> &str {
17+
let path = self.resolve_path(path);
18+
19+
self.files.entry(path).or_insert_with_key(|path| {
20+
std::fs::read_to_string(self.root.join(path)).expect("failed to read file") // FIXME
21+
})
22+
}
23+
24+
// FIXME: &str -> &str if possible
25+
fn resolve_path(&mut self, path: String) -> String {
26+
if path == "-" {
27+
// FIXME: proper error handling
28+
// FIXME: no cloning
29+
return self
30+
.last_path
31+
.clone()
32+
.expect("attempt to use `-` ('previous path') in the very first command");
33+
}
34+
35+
// While we could normalize the `path` at this point by
36+
// using `std::path::absolute`, it's likely not worth it.
37+
self.last_path = Some(path.clone());
38+
path
39+
}
40+
}

src/tools/htmldocck/src/check.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use crate::cache::Cache;
2+
use crate::error::DiagCtxt;
3+
use crate::{Command, CommandKind};
4+
5+
impl Command {
6+
pub(crate) fn check(self, cache: &mut Cache<'_>, dcx: &mut DiagCtxt) {
7+
match self.kind {
8+
CommandKind::HasFile { path } => todo!(),
9+
CommandKind::Has { path, pattern, text } => todo!(),
10+
CommandKind::HasDir { path } => todo!(),
11+
CommandKind::HasRaw { path, pattern } => {
12+
let file = cache.read_file(path);
13+
todo!()
14+
}
15+
CommandKind::Matches { path, pattern, text } => todo!(),
16+
CommandKind::MatchesRaw { path, pattern } => todo!(),
17+
CommandKind::Count { path, pattern, text, count } => todo!(),
18+
CommandKind::Files { path, files } => todo!(),
19+
CommandKind::Snapshot { name, path, pattern } => todo!(),
20+
}
21+
}
22+
}

src/tools/htmldocck/src/config.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use std::path::PathBuf;
2+
3+
use crate::error::DiagCtxt;
4+
5+
pub(crate) struct Config {
6+
/// The path to the directory that contains the generated HTML documentation.
7+
pub(crate) doc_dir: PathBuf,
8+
/// The path to the test file the docs were generated for and which may contain check commands.
9+
pub(crate) template: String,
10+
/// Whether to automatically update snapshot files.
11+
pub(crate) bless: bool,
12+
}
13+
14+
impl Config {
15+
pub(crate) fn parse(args: &[String], dcx: &mut DiagCtxt) -> Result<Self, ()> {
16+
const DOC_DIR_OPT: &str = "doc-dir";
17+
const TEMPLATE_OPT: &str = "template";
18+
const BLESS_FLAG: &str = "bless";
19+
20+
let mut opts = getopts::Options::new();
21+
opts.reqopt("", DOC_DIR_OPT, "Path to the documentation directory", "<PATH>")
22+
.reqopt("", TEMPLATE_OPT, "Path to the template file", "<PATH>")
23+
.optflag("", BLESS_FLAG, "Whether to automatically update snapshot files");
24+
25+
// We may not assume the presence of the first argument. On some platforms,
26+
// it's possible to pass an empty array of arguments to `execve`.
27+
let program = args.get(0).map(|arg| arg.as_str()).unwrap_or("htmldocck");
28+
let args = args.get(1..).unwrap_or_default();
29+
30+
match opts.parse(args) {
31+
Ok(matches) => Ok(Self {
32+
doc_dir: matches.opt_str(DOC_DIR_OPT).unwrap().into(),
33+
template: matches.opt_str(TEMPLATE_OPT).unwrap(),
34+
bless: matches.opt_present(BLESS_FLAG),
35+
}),
36+
Err(err) => {
37+
let mut err = err.to_string();
38+
err.push_str("\n\n");
39+
err.push_str(&opts.short_usage(program));
40+
err.push_str(&opts.usage(""));
41+
dcx.emit_noloc(&err);
42+
Err(())
43+
}
44+
}
45+
}
46+
}

src/tools/htmldocck/src/error.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::ops::Range;
2+
3+
use unicode_width::UnicodeWidthStr;
4+
5+
pub(crate) struct DiagCtxt {
6+
count: usize,
7+
}
8+
9+
impl DiagCtxt {
10+
pub(crate) fn scope(run: impl FnOnce(&mut Self) -> Result<(), ()>) -> Result<(), ()> {
11+
let mut dcx = Self::new();
12+
let result = run(&mut dcx);
13+
dcx.summarize();
14+
result.and(dcx.result())
15+
}
16+
17+
fn new() -> Self {
18+
Self { count: 0 }
19+
}
20+
21+
// FIXME: proper API, name
22+
pub(crate) fn emit_noloc(&mut self, message: &str) {
23+
// FIXME: use proper coloring library
24+
eprintln!("\x1b[31merror\x1b[0m: {message}");
25+
self.count += 1;
26+
}
27+
28+
// FIXME: proper API
29+
pub(crate) fn emit<'a>(
30+
&mut self,
31+
message: &str,
32+
line: &str,
33+
location: impl Into<Location>,
34+
help: impl Into<Option<&'a str>>,
35+
) {
36+
self._emit(message, line, location.into(), help.into())
37+
}
38+
39+
fn _emit(&mut self, message: &str, line: &str, location: Location, help: Option<&str>) {
40+
// FIXME: use proper coloring library
41+
eprintln!("\x1b[31merror\x1b[0m: {message}");
42+
eprintln!("\x1b[1;36m{} | \x1b[0m{line}", location.lineno);
43+
if let Some(range) = location.range {
44+
let underline_offset = line[..range.start].width();
45+
let underline_length = line[range].width();
46+
eprintln!(
47+
"\x1b[1;36m{} \x1b[0m\x1b[31m{}{}{}\x1b[0m",
48+
" ".repeat(location.lineno.ilog10() as usize + 1),
49+
" ".repeat(underline_offset),
50+
"^".repeat(underline_length),
51+
// FIXME: get rid of format here
52+
help.map(|help| format!(" help: {help}")).unwrap_or_default(),
53+
);
54+
}
55+
self.count += 1;
56+
}
57+
58+
pub(crate) fn result(&self) -> Result<(), ()> {
59+
match self.count {
60+
0 => Ok(()),
61+
_ => Err(()),
62+
}
63+
}
64+
65+
fn summarize(&self) {
66+
if self.count == 0 {
67+
return;
68+
}
69+
70+
eprintln!();
71+
eprintln!("encountered {} error{}", self.count, if self.count == 1 { "" } else { "s" },);
72+
}
73+
}
74+
75+
pub(crate) struct Location {
76+
/// The one-based line number.
77+
pub(crate) lineno: usize,
78+
// FIXME: docs
79+
// FIXME: check if we actually want Option<_> here
80+
pub(crate) range: Option<Range<usize>>,
81+
}
82+
83+
// FIXME: check if we want to keep this
84+
impl From<usize> for Location {
85+
fn from(lineno: usize) -> Self {
86+
Self { lineno, range: None }
87+
}
88+
}
89+
90+
// FIXME: check if we want to keep this (this way)
91+
impl From<(usize, Range<usize>)> for Location {
92+
fn from((lineno, range): (usize, Range<usize>)) -> Self {
93+
Self { lineno, range: Some(range) }
94+
}
95+
}

0 commit comments

Comments
 (0)