lint-docs: Add --validate flag to validate lint docs separately.
This commit is contained in:
parent
f17e6487b2
commit
d2d91b42a5
7 changed files with 115 additions and 23 deletions
|
@ -366,11 +366,25 @@ impl LintBuffer {
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The `{{produces}}` tag will be automatically replaced with the output from
|
/// The `{{produces}}` tag will be automatically replaced with the output from
|
||||||
/// the example by the build system. You can build and view the rustc book
|
/// the example by the build system. If the lint example is too complex to run
|
||||||
/// with `x.py doc --stage=1 src/doc/rustc --open`. If the lint example is too
|
/// as a simple example (for example, it needs an extern crate), mark the code
|
||||||
/// complex to run as a simple example (for example, it needs an extern
|
/// block with `ignore` and manually replace the `{{produces}}` line with the
|
||||||
/// crate), mark it with `ignore` and manually paste the expected output below
|
/// expected output in a `text` code block.
|
||||||
/// the example.
|
///
|
||||||
|
/// If this is a rustdoc-only lint, then only include a brief introduction
|
||||||
|
/// with a link with the text `[rustdoc book]` so that the validator knows
|
||||||
|
/// that this is for rustdoc only (see BROKEN_INTRA_DOC_LINKS as an example).
|
||||||
|
///
|
||||||
|
/// Commands to view and test the documentation:
|
||||||
|
///
|
||||||
|
/// * `./x.py doc --stage=1 src/doc/rustc --open`: Builds the rustc book and opens it.
|
||||||
|
/// * `./x.py test src/tools/lint-docs`: Validates that the lint docs have the
|
||||||
|
/// correct style, and that the code example actually emits the expected
|
||||||
|
/// lint.
|
||||||
|
///
|
||||||
|
/// If you have already built the compiler, and you want to make changes to
|
||||||
|
/// just the doc comments, then use the `--keep-stage=0` flag with the above
|
||||||
|
/// commands to avoid rebuilding the compiler.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! declare_lint {
|
macro_rules! declare_lint {
|
||||||
($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => (
|
($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => (
|
||||||
|
|
|
@ -413,6 +413,7 @@ impl<'a> Builder<'a> {
|
||||||
test::TheBook,
|
test::TheBook,
|
||||||
test::UnstableBook,
|
test::UnstableBook,
|
||||||
test::RustcBook,
|
test::RustcBook,
|
||||||
|
test::LintDocs,
|
||||||
test::RustcGuide,
|
test::RustcGuide,
|
||||||
test::EmbeddedBook,
|
test::EmbeddedBook,
|
||||||
test::EditionGuide,
|
test::EditionGuide,
|
||||||
|
|
|
@ -726,6 +726,7 @@ fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()>
|
||||||
pub struct RustcBook {
|
pub struct RustcBook {
|
||||||
pub compiler: Compiler,
|
pub compiler: Compiler,
|
||||||
pub target: TargetSelection,
|
pub target: TargetSelection,
|
||||||
|
pub validate: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Step for RustcBook {
|
impl Step for RustcBook {
|
||||||
|
@ -742,6 +743,7 @@ impl Step for RustcBook {
|
||||||
run.builder.ensure(RustcBook {
|
run.builder.ensure(RustcBook {
|
||||||
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
|
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
|
||||||
target: run.target,
|
target: run.target,
|
||||||
|
validate: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,6 +774,9 @@ impl Step for RustcBook {
|
||||||
if builder.config.verbose() {
|
if builder.config.verbose() {
|
||||||
cmd.arg("--verbose");
|
cmd.arg("--verbose");
|
||||||
}
|
}
|
||||||
|
if self.validate {
|
||||||
|
cmd.arg("--validate");
|
||||||
|
}
|
||||||
// If the lib directories are in an unusual location (changed in
|
// If the lib directories are in an unusual location (changed in
|
||||||
// config.toml), then this needs to explicitly update the dylib search
|
// config.toml), then this needs to explicitly update the dylib search
|
||||||
// path.
|
// path.
|
||||||
|
|
|
@ -2115,3 +2115,36 @@ impl Step for TierCheck {
|
||||||
try_run(builder, &mut cargo.into());
|
try_run(builder, &mut cargo.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct LintDocs {
|
||||||
|
pub compiler: Compiler,
|
||||||
|
pub target: TargetSelection,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Step for LintDocs {
|
||||||
|
type Output = ();
|
||||||
|
const DEFAULT: bool = true;
|
||||||
|
const ONLY_HOSTS: bool = true;
|
||||||
|
|
||||||
|
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||||
|
run.path("src/tools/lint-docs")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_run(run: RunConfig<'_>) {
|
||||||
|
run.builder.ensure(LintDocs {
|
||||||
|
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
|
||||||
|
target: run.target,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests that the lint examples in the rustc book generate the correct
|
||||||
|
/// lints and have the expected format.
|
||||||
|
fn run(self, builder: &Builder<'_>) {
|
||||||
|
builder.ensure(crate::doc::RustcBook {
|
||||||
|
compiler: self.compiler,
|
||||||
|
target: self.target,
|
||||||
|
validate: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::fmt::Write;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
/// Descriptions of rustc lint groups.
|
||||||
static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
|
static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
|
||||||
("unused", "Lints that detect things being declared but not used, or excess syntax"),
|
("unused", "Lints that detect things being declared but not used, or excess syntax"),
|
||||||
("rustdoc", "Rustdoc-specific lints"),
|
("rustdoc", "Rustdoc-specific lints"),
|
||||||
|
@ -86,17 +87,27 @@ impl<'a> LintExtractor<'a> {
|
||||||
result.push_str("|-------|-------------|-------|\n");
|
result.push_str("|-------|-------------|-------|\n");
|
||||||
result.push_str("| warnings | All lints that are set to issue warnings | See [warn-by-default] for the default set of warnings |\n");
|
result.push_str("| warnings | All lints that are set to issue warnings | See [warn-by-default] for the default set of warnings |\n");
|
||||||
for (group_name, group_lints) in groups {
|
for (group_name, group_lints) in groups {
|
||||||
let description = GROUP_DESCRIPTIONS
|
let description = match GROUP_DESCRIPTIONS.iter().find(|(n, _)| n == group_name) {
|
||||||
.iter()
|
Some((_, desc)) => desc,
|
||||||
.find(|(n, _)| n == group_name)
|
None if self.validate => {
|
||||||
.ok_or_else(|| {
|
return Err(format!(
|
||||||
format!(
|
|
||||||
"lint group `{}` does not have a description, \
|
"lint group `{}` does not have a description, \
|
||||||
please update the GROUP_DESCRIPTIONS list",
|
please update the GROUP_DESCRIPTIONS list in \
|
||||||
|
src/tools/lint-docs/src/groups.rs",
|
||||||
group_name
|
group_name
|
||||||
)
|
)
|
||||||
})?
|
.into());
|
||||||
.1;
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!(
|
||||||
|
"warning: lint group `{}` is missing from the GROUP_DESCRIPTIONS list\n\
|
||||||
|
If this is a new lint group, please update the GROUP_DESCRIPTIONS in \
|
||||||
|
src/tools/lint-docs/src/groups.rs",
|
||||||
|
group_name
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
to_link.extend(group_lints);
|
to_link.extend(group_lints);
|
||||||
let brackets: Vec<_> = group_lints.iter().map(|l| format!("[{}]", l)).collect();
|
let brackets: Vec<_> = group_lints.iter().map(|l| format!("[{}]", l)).collect();
|
||||||
write!(result, "| {} | {} | {} |\n", group_name, description, brackets.join(", "))
|
write!(result, "| {} | {} | {} |\n", group_name, description, brackets.join(", "))
|
||||||
|
|
|
@ -19,6 +19,8 @@ pub struct LintExtractor<'a> {
|
||||||
pub rustc_target: &'a str,
|
pub rustc_target: &'a str,
|
||||||
/// Verbose output.
|
/// Verbose output.
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
|
/// Validate the style and the code example.
|
||||||
|
pub validate: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Lint {
|
struct Lint {
|
||||||
|
@ -122,7 +124,7 @@ impl<'a> LintExtractor<'a> {
|
||||||
let contents = fs::read_to_string(path)
|
let contents = fs::read_to_string(path)
|
||||||
.map_err(|e| format!("could not read {}: {}", path.display(), e))?;
|
.map_err(|e| format!("could not read {}: {}", path.display(), e))?;
|
||||||
let mut lines = contents.lines().enumerate();
|
let mut lines = contents.lines().enumerate();
|
||||||
loop {
|
'outer: loop {
|
||||||
// Find a lint declaration.
|
// Find a lint declaration.
|
||||||
let lint_start = loop {
|
let lint_start = loop {
|
||||||
match lines.next() {
|
match lines.next() {
|
||||||
|
@ -158,12 +160,22 @@ impl<'a> LintExtractor<'a> {
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
if doc_lines.is_empty() {
|
if doc_lines.is_empty() {
|
||||||
return Err(format!(
|
if self.validate {
|
||||||
"did not find doc lines for lint `{}` in {}",
|
return Err(format!(
|
||||||
name,
|
"did not find doc lines for lint `{}` in {}",
|
||||||
path.display()
|
name,
|
||||||
)
|
path.display()
|
||||||
.into());
|
)
|
||||||
|
.into());
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"warning: lint `{}` in {} does not define any doc lines, \
|
||||||
|
these are required for the lint documentation",
|
||||||
|
name,
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break (doc_lines, name);
|
break (doc_lines, name);
|
||||||
}
|
}
|
||||||
|
@ -234,13 +246,26 @@ impl<'a> LintExtractor<'a> {
|
||||||
// Rustdoc lints are documented in the rustdoc book, don't check these.
|
// Rustdoc lints are documented in the rustdoc book, don't check these.
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
lint.check_style()?;
|
if self.validate {
|
||||||
|
lint.check_style()?;
|
||||||
|
}
|
||||||
// Unfortunately some lints have extra requirements that this simple test
|
// Unfortunately some lints have extra requirements that this simple test
|
||||||
// setup can't handle (like extern crates). An alternative is to use a
|
// setup can't handle (like extern crates). An alternative is to use a
|
||||||
// separate test suite, and use an include mechanism such as mdbook's
|
// separate test suite, and use an include mechanism such as mdbook's
|
||||||
// `{{#rustdoc_include}}`.
|
// `{{#rustdoc_include}}`.
|
||||||
if !lint.is_ignored() {
|
if !lint.is_ignored() {
|
||||||
self.replace_produces(lint)?;
|
if let Err(e) = self.replace_produces(lint) {
|
||||||
|
if self.validate {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
eprintln!(
|
||||||
|
"warning: the code example in lint `{}` in {} failed to \
|
||||||
|
generate the expected output: {}",
|
||||||
|
lint.name,
|
||||||
|
lint.path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(e) = doit() {
|
if let Err(e) = doit() {
|
||||||
println!("error: {}", e);
|
eprintln!("error: {}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
|
||||||
let mut rustc_path = None;
|
let mut rustc_path = None;
|
||||||
let mut rustc_target = None;
|
let mut rustc_target = None;
|
||||||
let mut verbose = false;
|
let mut verbose = false;
|
||||||
|
let mut validate = false;
|
||||||
while let Some(arg) = args.next() {
|
while let Some(arg) = args.next() {
|
||||||
match arg.as_str() {
|
match arg.as_str() {
|
||||||
"--src" => {
|
"--src" => {
|
||||||
|
@ -42,6 +43,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
"-v" | "--verbose" => verbose = true,
|
"-v" | "--verbose" => verbose = true,
|
||||||
|
"--validate" => validate = true,
|
||||||
s => return Err(format!("unexpected argument `{}`", s).into()),
|
s => return Err(format!("unexpected argument `{}`", s).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +65,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
|
||||||
rustc_path: &rustc_path.unwrap(),
|
rustc_path: &rustc_path.unwrap(),
|
||||||
rustc_target: &rustc_target.unwrap(),
|
rustc_target: &rustc_target.unwrap(),
|
||||||
verbose,
|
verbose,
|
||||||
|
validate,
|
||||||
};
|
};
|
||||||
le.extract_lint_docs()
|
le.extract_lint_docs()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue