Skip to content

Commit d2d91b4

Browse files
committed
lint-docs: Add --validate flag to validate lint docs separately.
1 parent f17e648 commit d2d91b4

File tree

7 files changed

+115
-23
lines changed

7 files changed

+115
-23
lines changed

compiler/rustc_lint_defs/src/lib.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -366,11 +366,25 @@ impl LintBuffer {
366366
/// ```
367367
///
368368
/// The `{{produces}}` tag will be automatically replaced with the output from
369-
/// the example by the build system. You can build and view the rustc book
370-
/// with `x.py doc --stage=1 src/doc/rustc --open`. If the lint example is too
371-
/// complex to run as a simple example (for example, it needs an extern
372-
/// crate), mark it with `ignore` and manually paste the expected output below
373-
/// the example.
369+
/// the example by the build system. If the lint example is too complex to run
370+
/// as a simple example (for example, it needs an extern crate), mark the code
371+
/// block with `ignore` and manually replace the `{{produces}}` line with the
372+
/// expected output in a `text` code block.
373+
///
374+
/// If this is a rustdoc-only lint, then only include a brief introduction
375+
/// with a link with the text `[rustdoc book]` so that the validator knows
376+
/// that this is for rustdoc only (see BROKEN_INTRA_DOC_LINKS as an example).
377+
///
378+
/// Commands to view and test the documentation:
379+
///
380+
/// * `./x.py doc --stage=1 src/doc/rustc --open`: Builds the rustc book and opens it.
381+
/// * `./x.py test src/tools/lint-docs`: Validates that the lint docs have the
382+
/// correct style, and that the code example actually emits the expected
383+
/// lint.
384+
///
385+
/// If you have already built the compiler, and you want to make changes to
386+
/// just the doc comments, then use the `--keep-stage=0` flag with the above
387+
/// commands to avoid rebuilding the compiler.
374388
#[macro_export]
375389
macro_rules! declare_lint {
376390
($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => (

src/bootstrap/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ impl<'a> Builder<'a> {
413413
test::TheBook,
414414
test::UnstableBook,
415415
test::RustcBook,
416+
test::LintDocs,
416417
test::RustcGuide,
417418
test::EmbeddedBook,
418419
test::EditionGuide,

src/bootstrap/doc.rs

+5
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,7 @@ fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()>
726726
pub struct RustcBook {
727727
pub compiler: Compiler,
728728
pub target: TargetSelection,
729+
pub validate: bool,
729730
}
730731

731732
impl Step for RustcBook {
@@ -742,6 +743,7 @@ impl Step for RustcBook {
742743
run.builder.ensure(RustcBook {
743744
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
744745
target: run.target,
746+
validate: false,
745747
});
746748
}
747749

@@ -772,6 +774,9 @@ impl Step for RustcBook {
772774
if builder.config.verbose() {
773775
cmd.arg("--verbose");
774776
}
777+
if self.validate {
778+
cmd.arg("--validate");
779+
}
775780
// If the lib directories are in an unusual location (changed in
776781
// config.toml), then this needs to explicitly update the dylib search
777782
// path.

src/bootstrap/test.rs

+33
Original file line numberDiff line numberDiff line change
@@ -2115,3 +2115,36 @@ impl Step for TierCheck {
21152115
try_run(builder, &mut cargo.into());
21162116
}
21172117
}
2118+
2119+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2120+
pub struct LintDocs {
2121+
pub compiler: Compiler,
2122+
pub target: TargetSelection,
2123+
}
2124+
2125+
impl Step for LintDocs {
2126+
type Output = ();
2127+
const DEFAULT: bool = true;
2128+
const ONLY_HOSTS: bool = true;
2129+
2130+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2131+
run.path("src/tools/lint-docs")
2132+
}
2133+
2134+
fn make_run(run: RunConfig<'_>) {
2135+
run.builder.ensure(LintDocs {
2136+
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
2137+
target: run.target,
2138+
});
2139+
}
2140+
2141+
/// Tests that the lint examples in the rustc book generate the correct
2142+
/// lints and have the expected format.
2143+
fn run(self, builder: &Builder<'_>) {
2144+
builder.ensure(crate::doc::RustcBook {
2145+
compiler: self.compiler,
2146+
target: self.target,
2147+
validate: true,
2148+
});
2149+
}
2150+
}

src/tools/lint-docs/src/groups.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::fmt::Write;
55
use std::fs;
66
use std::process::Command;
77

8+
/// Descriptions of rustc lint groups.
89
static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
910
("unused", "Lints that detect things being declared but not used, or excess syntax"),
1011
("rustdoc", "Rustdoc-specific lints"),
@@ -86,17 +87,27 @@ impl<'a> LintExtractor<'a> {
8687
result.push_str("|-------|-------------|-------|\n");
8788
result.push_str("| warnings | All lints that are set to issue warnings | See [warn-by-default] for the default set of warnings |\n");
8889
for (group_name, group_lints) in groups {
89-
let description = GROUP_DESCRIPTIONS
90-
.iter()
91-
.find(|(n, _)| n == group_name)
92-
.ok_or_else(|| {
93-
format!(
90+
let description = match GROUP_DESCRIPTIONS.iter().find(|(n, _)| n == group_name) {
91+
Some((_, desc)) => desc,
92+
None if self.validate => {
93+
return Err(format!(
9494
"lint group `{}` does not have a description, \
95-
please update the GROUP_DESCRIPTIONS list",
95+
please update the GROUP_DESCRIPTIONS list in \
96+
src/tools/lint-docs/src/groups.rs",
9697
group_name
9798
)
98-
})?
99-
.1;
99+
.into());
100+
}
101+
None => {
102+
eprintln!(
103+
"warning: lint group `{}` is missing from the GROUP_DESCRIPTIONS list\n\
104+
If this is a new lint group, please update the GROUP_DESCRIPTIONS in \
105+
src/tools/lint-docs/src/groups.rs",
106+
group_name
107+
);
108+
continue;
109+
}
110+
};
100111
to_link.extend(group_lints);
101112
let brackets: Vec<_> = group_lints.iter().map(|l| format!("[{}]", l)).collect();
102113
write!(result, "| {} | {} | {} |\n", group_name, description, brackets.join(", "))

src/tools/lint-docs/src/lib.rs

+34-9
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub struct LintExtractor<'a> {
1919
pub rustc_target: &'a str,
2020
/// Verbose output.
2121
pub verbose: bool,
22+
/// Validate the style and the code example.
23+
pub validate: bool,
2224
}
2325

2426
struct Lint {
@@ -122,7 +124,7 @@ impl<'a> LintExtractor<'a> {
122124
let contents = fs::read_to_string(path)
123125
.map_err(|e| format!("could not read {}: {}", path.display(), e))?;
124126
let mut lines = contents.lines().enumerate();
125-
loop {
127+
'outer: loop {
126128
// Find a lint declaration.
127129
let lint_start = loop {
128130
match lines.next() {
@@ -158,12 +160,22 @@ impl<'a> LintExtractor<'a> {
158160
)
159161
})?;
160162
if doc_lines.is_empty() {
161-
return Err(format!(
162-
"did not find doc lines for lint `{}` in {}",
163-
name,
164-
path.display()
165-
)
166-
.into());
163+
if self.validate {
164+
return Err(format!(
165+
"did not find doc lines for lint `{}` in {}",
166+
name,
167+
path.display()
168+
)
169+
.into());
170+
} else {
171+
eprintln!(
172+
"warning: lint `{}` in {} does not define any doc lines, \
173+
these are required for the lint documentation",
174+
name,
175+
path.display()
176+
);
177+
continue 'outer;
178+
}
167179
}
168180
break (doc_lines, name);
169181
}
@@ -234,13 +246,26 @@ impl<'a> LintExtractor<'a> {
234246
// Rustdoc lints are documented in the rustdoc book, don't check these.
235247
return Ok(());
236248
}
237-
lint.check_style()?;
249+
if self.validate {
250+
lint.check_style()?;
251+
}
238252
// Unfortunately some lints have extra requirements that this simple test
239253
// setup can't handle (like extern crates). An alternative is to use a
240254
// separate test suite, and use an include mechanism such as mdbook's
241255
// `{{#rustdoc_include}}`.
242256
if !lint.is_ignored() {
243-
self.replace_produces(lint)?;
257+
if let Err(e) = self.replace_produces(lint) {
258+
if self.validate {
259+
return Err(e);
260+
}
261+
eprintln!(
262+
"warning: the code example in lint `{}` in {} failed to \
263+
generate the expected output: {}",
264+
lint.name,
265+
lint.path.display(),
266+
e
267+
);
268+
}
244269
}
245270
Ok(())
246271
}

src/tools/lint-docs/src/main.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::path::PathBuf;
33

44
fn main() {
55
if let Err(e) = doit() {
6-
println!("error: {}", e);
6+
eprintln!("error: {}", e);
77
std::process::exit(1);
88
}
99
}
@@ -15,6 +15,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
1515
let mut rustc_path = None;
1616
let mut rustc_target = None;
1717
let mut verbose = false;
18+
let mut validate = false;
1819
while let Some(arg) = args.next() {
1920
match arg.as_str() {
2021
"--src" => {
@@ -42,6 +43,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
4243
};
4344
}
4445
"-v" | "--verbose" => verbose = true,
46+
"--validate" => validate = true,
4547
s => return Err(format!("unexpected argument `{}`", s).into()),
4648
}
4749
}
@@ -63,6 +65,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
6365
rustc_path: &rustc_path.unwrap(),
6466
rustc_target: &rustc_target.unwrap(),
6567
verbose,
68+
validate,
6669
};
6770
le.extract_lint_docs()
6871
}

0 commit comments

Comments
 (0)