|  | use std::collections::{BTreeMap, BTreeSet}; | 
|  | use std::error::Error; | 
|  | use std::fmt::Write; | 
|  | use std::fs; | 
|  | use std::process::Command; | 
|  |  | 
|  | use crate::{Lint, LintExtractor}; | 
|  |  | 
|  | /// Descriptions of rustc lint groups. | 
|  | static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[ | 
|  | ("unused", "Lints that detect things being declared but not used, or excess syntax"), | 
|  | ("let-underscore", "Lints that detect wildcard let bindings that are likely to be invalid"), | 
|  | ("rustdoc", "Rustdoc-specific lints"), | 
|  | ("rust-2018-idioms", "Lints to nudge you toward idiomatic features of Rust 2018"), | 
|  | ("nonstandard-style", "Violation of standard naming conventions"), | 
|  | ("future-incompatible", "Lints that detect code that has future-compatibility problems"), | 
|  | ("rust-2018-compatibility", "Lints used to transition code from the 2015 edition to 2018"), | 
|  | ("rust-2021-compatibility", "Lints used to transition code from the 2018 edition to 2021"), | 
|  | ("rust-2024-compatibility", "Lints used to transition code from the 2021 edition to 2024"), | 
|  | ( | 
|  | "refining-impl-trait", | 
|  | "Detects refinement of `impl Trait` return types by trait implementations", | 
|  | ), | 
|  | ( | 
|  | "keyword-idents", | 
|  | "Lints that detect identifiers which will be come keywords in later editions", | 
|  | ), | 
|  | ("deprecated-safe", "Lints for functions which were erroneously marked as safe in the past"), | 
|  | ( | 
|  | "unknown-or-malformed-diagnostic-attributes", | 
|  | "detects unknown or malformed diagnostic attributes", | 
|  | ), | 
|  | ]; | 
|  |  | 
|  | type LintGroups = BTreeMap<String, BTreeSet<String>>; | 
|  |  | 
|  | impl<'a> LintExtractor<'a> { | 
|  | /// Updates the documentation of lint groups. | 
|  | pub(crate) fn generate_group_docs(&self, lints: &[Lint]) -> Result<(), Box<dyn Error>> { | 
|  | let groups = self.collect_groups()?; | 
|  | let groups_path = self.out_path.join("groups.md"); | 
|  | let contents = fs::read_to_string(&groups_path) | 
|  | .map_err(|e| format!("could not read {}: {}", groups_path.display(), e))?; | 
|  | let new_contents = | 
|  | contents.replace("{{groups-table}}", &self.make_groups_table(lints, &groups)?); | 
|  | // Delete the output because bootstrap uses hard links in its copies. | 
|  | let _ = fs::remove_file(&groups_path); | 
|  | fs::write(&groups_path, new_contents) | 
|  | .map_err(|e| format!("could not write to {}: {}", groups_path.display(), e))?; | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// Collects the group names from rustc. | 
|  | fn collect_groups(&self) -> Result<LintGroups, Box<dyn Error>> { | 
|  | let mut result = BTreeMap::new(); | 
|  | let mut cmd = Command::new(self.rustc_path); | 
|  | cmd.arg("-Whelp"); | 
|  | let output = cmd.output().map_err(|e| format!("failed to run command {:?}\n{}", cmd, e))?; | 
|  | if !output.status.success() { | 
|  | return Err(format!( | 
|  | "failed to collect lint info: failed to run {cmd:?}: {:?}\n--- stderr\n{}--- stdout\n{}\n", | 
|  | output.status, | 
|  | std::str::from_utf8(&output.stderr).unwrap(), | 
|  | std::str::from_utf8(&output.stdout).unwrap(), | 
|  | ) | 
|  | .into()); | 
|  | } | 
|  | let stdout = std::str::from_utf8(&output.stdout).unwrap(); | 
|  | let lines = stdout.lines(); | 
|  | let group_start = lines.skip_while(|line| !line.contains("groups provided")).skip(1); | 
|  | let table_start = group_start.skip_while(|line| !line.contains("----")).skip(1); | 
|  | for line in table_start { | 
|  | if line.is_empty() { | 
|  | break; | 
|  | } | 
|  | let mut parts = line.trim().splitn(2, ' '); | 
|  | let name = parts.next().expect("name in group"); | 
|  | if name == "warnings" { | 
|  | // This is special. | 
|  | continue; | 
|  | } | 
|  | let lints = parts | 
|  | .next() | 
|  | .ok_or_else(|| format!("expected lints following name, got `{}`", line))?; | 
|  | let lints = lints.split(',').map(|l| l.trim().to_string()).collect(); | 
|  | assert!(result.insert(name.to_string(), lints).is_none()); | 
|  | } | 
|  | if result.is_empty() { | 
|  | return Err( | 
|  | format!("expected at least one group in -Whelp output, got:\n{}", stdout).into() | 
|  | ); | 
|  | } | 
|  | Ok(result) | 
|  | } | 
|  |  | 
|  | fn make_groups_table( | 
|  | &self, | 
|  | lints: &[Lint], | 
|  | groups: &LintGroups, | 
|  | ) -> Result<String, Box<dyn Error>> { | 
|  | let mut result = String::new(); | 
|  | let mut to_link = Vec::new(); | 
|  | result.push_str("| Group | Description | Lints |\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"); | 
|  | for (group_name, group_lints) in groups { | 
|  | let description = match GROUP_DESCRIPTIONS.iter().find(|(n, _)| n == group_name) { | 
|  | Some((_, desc)) => desc, | 
|  | None if self.validate => { | 
|  | return Err(format!( | 
|  | "lint group `{}` does not have a description, \ | 
|  | please update the GROUP_DESCRIPTIONS list in \ | 
|  | src/tools/lint-docs/src/groups.rs", | 
|  | group_name | 
|  | ) | 
|  | .into()); | 
|  | } | 
|  | 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); | 
|  | let brackets: Vec<_> = group_lints.iter().map(|l| format!("[{}]", l)).collect(); | 
|  | writeln!(result, "| {} | {} | {} |", group_name, description, brackets.join(", ")) | 
|  | .unwrap(); | 
|  | } | 
|  | result.push('\n'); | 
|  | result.push_str("[warn-by-default]: listing/warn-by-default.md\n"); | 
|  | for lint_name in to_link { | 
|  | let lint_def = match lints.iter().find(|l| l.name == lint_name.replace('-', "_")) { | 
|  | Some(def) => def, | 
|  | None => { | 
|  | let msg = format!( | 
|  | "`rustc -W help` defined lint `{}` but that lint does not \ | 
|  | appear to exist\n\ | 
|  | Check that the lint definition includes the appropriate doc comments.", | 
|  | lint_name | 
|  | ); | 
|  | if self.validate { | 
|  | return Err(msg.into()); | 
|  | } else { | 
|  | eprintln!("warning: {}", msg); | 
|  | continue; | 
|  | } | 
|  | } | 
|  | }; | 
|  | writeln!( | 
|  | result, | 
|  | "[{}]: listing/{}#{}", | 
|  | lint_name, | 
|  | lint_def.level.doc_filename(), | 
|  | lint_name | 
|  | ) | 
|  | .unwrap(); | 
|  | } | 
|  | Ok(result) | 
|  | } | 
|  | } |