]> git.proxmox.com Git - rustc.git/blame - src/tools/lint-docs/src/groups.rs
New upstream version 1.51.0+dfsg1
[rustc.git] / src / tools / lint-docs / src / groups.rs
CommitLineData
fc512014 1use crate::{Lint, LintExtractor};
1b1a35ee
XL
2use std::collections::{BTreeMap, BTreeSet};
3use std::error::Error;
4use std::fmt::Write;
5use std::fs;
1b1a35ee
XL
6use std::process::Command;
7
fc512014 8/// Descriptions of rustc lint groups.
1b1a35ee
XL
9static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
10 ("unused", "Lints that detect things being declared but not used, or excess syntax"),
11 ("rustdoc", "Rustdoc-specific lints"),
12 ("rust-2018-idioms", "Lints to nudge you toward idiomatic features of Rust 2018"),
13 ("nonstandard-style", "Violation of standard naming conventions"),
14 ("future-incompatible", "Lints that detect code that has future-compatibility problems"),
15 ("rust-2018-compatibility", "Lints used to transition code from the 2015 edition to 2018"),
16];
17
1b1a35ee
XL
18type LintGroups = BTreeMap<String, BTreeSet<String>>;
19
fc512014
XL
20impl<'a> LintExtractor<'a> {
21 /// Updates the documentation of lint groups.
22 pub(crate) fn generate_group_docs(&self, lints: &[Lint]) -> Result<(), Box<dyn Error>> {
23 let groups = self.collect_groups()?;
24 let groups_path = self.out_path.join("groups.md");
25 let contents = fs::read_to_string(&groups_path)
26 .map_err(|e| format!("could not read {}: {}", groups_path.display(), e))?;
27 let new_contents =
28 contents.replace("{{groups-table}}", &self.make_groups_table(lints, &groups)?);
29 // Delete the output because rustbuild uses hard links in its copies.
30 let _ = fs::remove_file(&groups_path);
31 fs::write(&groups_path, new_contents)
32 .map_err(|e| format!("could not write to {}: {}", groups_path.display(), e))?;
33 Ok(())
1b1a35ee 34 }
fc512014
XL
35
36 /// Collects the group names from rustc.
37 fn collect_groups(&self) -> Result<LintGroups, Box<dyn Error>> {
38 let mut result = BTreeMap::new();
39 let mut cmd = Command::new(self.rustc_path);
40 cmd.arg("-Whelp");
41 let output = cmd.output().map_err(|e| format!("failed to run command {:?}\n{}", cmd, e))?;
42 if !output.status.success() {
43 return Err(format!(
44 "failed to collect lint info: {:?}\n--- stderr\n{}--- stdout\n{}\n",
45 output.status,
46 std::str::from_utf8(&output.stderr).unwrap(),
47 std::str::from_utf8(&output.stdout).unwrap(),
48 )
49 .into());
1b1a35ee 50 }
fc512014
XL
51 let stdout = std::str::from_utf8(&output.stdout).unwrap();
52 let lines = stdout.lines();
53 let group_start = lines.skip_while(|line| !line.contains("groups provided")).skip(1);
54 let table_start = group_start.skip_while(|line| !line.contains("----")).skip(1);
55 for line in table_start {
56 if line.is_empty() {
57 break;
58 }
59 let mut parts = line.trim().splitn(2, ' ');
60 let name = parts.next().expect("name in group");
61 if name == "warnings" {
62 // This is special.
63 continue;
64 }
65 let lints = parts
66 .next()
67 .ok_or_else(|| format!("expected lints following name, got `{}`", line))?;
68 let lints = lints.split(',').map(|l| l.trim().to_string()).collect();
69 assert!(result.insert(name.to_string(), lints).is_none());
1b1a35ee 70 }
fc512014
XL
71 if result.is_empty() {
72 return Err(
73 format!("expected at least one group in -Whelp output, got:\n{}", stdout).into()
74 );
75 }
76 Ok(result)
1b1a35ee 77 }
1b1a35ee 78
fc512014
XL
79 fn make_groups_table(
80 &self,
81 lints: &[Lint],
82 groups: &LintGroups,
83 ) -> Result<String, Box<dyn Error>> {
84 let mut result = String::new();
85 let mut to_link = Vec::new();
86 result.push_str("| Group | Description | Lints |\n");
87 result.push_str("|-------|-------------|-------|\n");
88 result.push_str("| warnings | All lints that are set to issue warnings | See [warn-by-default] for the default set of warnings |\n");
89 for (group_name, group_lints) in groups {
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!(
94 "lint group `{}` does not have a description, \
95 please update the GROUP_DESCRIPTIONS list in \
96 src/tools/lint-docs/src/groups.rs",
97 group_name
98 )
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 };
111 to_link.extend(group_lints);
112 let brackets: Vec<_> = group_lints.iter().map(|l| format!("[{}]", l)).collect();
113 write!(result, "| {} | {} | {} |\n", group_name, description, brackets.join(", "))
114 .unwrap();
115 }
116 result.push('\n');
117 result.push_str("[warn-by-default]: listing/warn-by-default.md\n");
118 for lint_name in to_link {
5869c6ff
XL
119 let lint_def = match lints.iter().find(|l| l.name == lint_name.replace("-", "_")) {
120 Some(def) => def,
121 None => {
122 let msg = format!(
123 "`rustc -W help` defined lint `{}` but that lint does not \
124 appear to exist\n\
125 Check that the lint definition includes the appropriate doc comments.",
fc512014 126 lint_name
5869c6ff
XL
127 );
128 if self.validate {
129 return Err(msg.into());
130 } else {
131 eprintln!("warning: {}", msg);
132 continue;
133 }
134 }
135 };
fc512014
XL
136 write!(
137 result,
138 "[{}]: listing/{}#{}\n",
139 lint_name,
140 lint_def.level.doc_filename(),
141 lint_name
142 )
143 .unwrap();
144 }
145 Ok(result)
1b1a35ee 146 }
1b1a35ee 147}