1
Fork 0

lint-docs: Add --validate flag to validate lint docs separately.

This commit is contained in:
Eric Huss 2020-11-28 13:29:51 -08:00
parent f17e6487b2
commit d2d91b42a5
7 changed files with 115 additions and 23 deletions

View file

@ -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) => (

View file

@ -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,

View file

@ -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.

View file

@ -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,
});
}
}

View file

@ -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(", "))

View file

@ -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(())
} }

View file

@ -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()
} }