Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'xtask/src/codegen/lints.rs')
-rw-r--r--xtask/src/codegen/lints.rs380
1 files changed, 303 insertions, 77 deletions
diff --git a/xtask/src/codegen/lints.rs b/xtask/src/codegen/lints.rs
index f097b5817b..b1a7c2fb27 100644
--- a/xtask/src/codegen/lints.rs
+++ b/xtask/src/codegen/lints.rs
@@ -1,7 +1,15 @@
//! Generates descriptor structures for unstable features from the unstable book
//! and lints from rustc, rustdoc, and clippy.
-use std::{borrow::Cow, fs, path::Path};
+#![allow(clippy::disallowed_types)]
+use std::{
+ collections::{hash_map, HashMap},
+ fs,
+ path::Path,
+ str::FromStr,
+};
+
+use edition::Edition;
use stdx::format_to;
use xshell::{cmd, Shell};
@@ -36,10 +44,17 @@ pub(crate) fn generate(check: bool) {
let mut contents = String::from(
r"
+use span::Edition;
+
+use crate::Severity;
+
#[derive(Clone)]
pub struct Lint {
pub label: &'static str,
pub description: &'static str,
+ pub default_severity: Severity,
+ pub warn_since: Option<Edition>,
+ pub deny_since: Option<Edition>,
}
pub struct LintGroup {
@@ -68,7 +83,7 @@ pub struct LintGroup {
let lints_json = project_root().join("./target/clippy_lints.json");
cmd!(
sh,
- "curl https://rust-lang.github.io/rust-clippy/master/lints.json --output {lints_json}"
+ "curl https://rust-lang.github.io/rust-clippy/stable/lints.json --output {lints_json}"
)
.run()
.unwrap();
@@ -85,6 +100,48 @@ pub struct LintGroup {
);
}
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
+enum Severity {
+ Allow,
+ Warn,
+ Deny,
+}
+
+impl std::fmt::Display for Severity {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "Severity::{}",
+ match self {
+ Severity::Allow => "Allow",
+ Severity::Warn => "Warning",
+ Severity::Deny => "Error",
+ }
+ )
+ }
+}
+
+impl FromStr for Severity {
+ type Err = &'static str;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "allow" => Ok(Self::Allow),
+ "warn" => Ok(Self::Warn),
+ "deny" => Ok(Self::Deny),
+ _ => Err("invalid severity"),
+ }
+ }
+}
+
+#[derive(Debug)]
+struct Lint {
+ description: String,
+ default_severity: Severity,
+ warn_since: Option<Edition>,
+ deny_since: Option<Edition>,
+}
+
/// Parses the output of `rustdoc -Whelp` and prints `Lint` and `LintGroup` constants into `buf`.
///
/// As of writing, the output of `rustc -Whelp` (not rustdoc) has the following format:
@@ -108,52 +165,203 @@ pub struct LintGroup {
/// `rustdoc -Whelp` (and any other custom `rustc` driver) adds another two
/// tables after the `rustc` ones, with a different title but the same format.
fn generate_lint_descriptor(sh: &Shell, buf: &mut String) {
- let stdout = cmd!(sh, "rustdoc -Whelp").read().unwrap();
- let lints_pat = "---- ------- -------\n";
- let lint_groups_pat = "---- ---------\n";
- let lints = find_and_slice(&stdout, lints_pat);
- let lint_groups = find_and_slice(lints, lint_groups_pat);
- let lints_rustdoc = find_and_slice(lint_groups, lints_pat);
- let lint_groups_rustdoc = find_and_slice(lints_rustdoc, lint_groups_pat);
+ fn get_lints_as_text(
+ stdout: &str,
+ ) -> (
+ impl Iterator<Item = (String, &str, Severity)> + '_,
+ impl Iterator<Item = (String, Lint, impl Iterator<Item = String> + '_)> + '_,
+ impl Iterator<Item = (String, &str, Severity)> + '_,
+ impl Iterator<Item = (String, Lint, impl Iterator<Item = String> + '_)> + '_,
+ ) {
+ let lints_pat = "---- ------- -------\n";
+ let lint_groups_pat = "---- ---------\n";
+ let lints = find_and_slice(stdout, lints_pat);
+ let lint_groups = find_and_slice(lints, lint_groups_pat);
+ let lints_rustdoc = find_and_slice(lint_groups, lints_pat);
+ let lint_groups_rustdoc = find_and_slice(lints_rustdoc, lint_groups_pat);
+
+ let lints = lints.lines().take_while(|l| !l.is_empty()).map(|line| {
+ let (name, rest) = line.trim().split_once(char::is_whitespace).unwrap();
+ let (severity, description) = rest.trim().split_once(char::is_whitespace).unwrap();
+ (name.trim().replace('-', "_"), description.trim(), severity.parse().unwrap())
+ });
+ let lint_groups = lint_groups.lines().take_while(|l| !l.is_empty()).map(|line| {
+ let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap();
+ let label = name.trim().replace('-', "_");
+ let lint = Lint {
+ description: format!("lint group for: {}", lints.trim()),
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ };
+ let children = lints
+ .split_ascii_whitespace()
+ .map(|s| s.trim().trim_matches(',').replace('-', "_"));
+ (label, lint, children)
+ });
- buf.push_str(r#"pub const DEFAULT_LINTS: &[Lint] = &["#);
- buf.push('\n');
+ let lints_rustdoc = lints_rustdoc.lines().take_while(|l| !l.is_empty()).map(|line| {
+ let (name, rest) = line.trim().split_once(char::is_whitespace).unwrap();
+ let (severity, description) = rest.trim().split_once(char::is_whitespace).unwrap();
+ (name.trim().replace('-', "_"), description.trim(), severity.parse().unwrap())
+ });
+ let lint_groups_rustdoc =
+ lint_groups_rustdoc.lines().take_while(|l| !l.is_empty()).map(|line| {
+ let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap();
+ let label = name.trim().replace('-', "_");
+ let lint = Lint {
+ description: format!("lint group for: {}", lints.trim()),
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ };
+ let children = lints
+ .split_ascii_whitespace()
+ .map(|s| s.trim().trim_matches(',').replace('-', "_"));
+ (label, lint, children)
+ });
- let lints = lints.lines().take_while(|l| !l.is_empty()).map(|line| {
- let (name, rest) = line.trim().split_once(char::is_whitespace).unwrap();
- let (_default_level, description) = rest.trim().split_once(char::is_whitespace).unwrap();
- (name.trim(), Cow::Borrowed(description.trim()), vec![])
- });
- let lint_groups = lint_groups.lines().take_while(|l| !l.is_empty()).map(|line| {
- let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap();
- (
- name.trim(),
- format!("lint group for: {}", lints.trim()).into(),
- lints
- .split_ascii_whitespace()
- .map(|s| s.trim().trim_matches(',').replace('-', "_"))
- .collect(),
- )
- });
+ (lints, lint_groups, lints_rustdoc, lint_groups_rustdoc)
+ }
- let mut lints = lints.chain(lint_groups).collect::<Vec<_>>();
- lints.sort_by(|(ident, ..), (ident2, ..)| ident.cmp(ident2));
+ fn insert_lints<'a>(
+ edition: Edition,
+ lints_map: &mut HashMap<String, Lint>,
+ lint_groups_map: &mut HashMap<String, (Lint, Vec<String>)>,
+ lints: impl Iterator<Item = (String, &'a str, Severity)>,
+ lint_groups: impl Iterator<Item = (String, Lint, impl Iterator<Item = String>)>,
+ ) {
+ for (lint_name, lint_description, lint_severity) in lints {
+ let lint = lints_map.entry(lint_name).or_insert_with(|| Lint {
+ description: lint_description.to_owned(),
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ });
+ if lint_severity == Severity::Warn
+ && lint.warn_since.is_none()
+ && lint.default_severity < Severity::Warn
+ {
+ lint.warn_since = Some(edition);
+ }
+ if lint_severity == Severity::Deny
+ && lint.deny_since.is_none()
+ && lint.default_severity < Severity::Deny
+ {
+ lint.deny_since = Some(edition);
+ }
+ }
- for (name, description, ..) in &lints {
- push_lint_completion(buf, &name.replace('-', "_"), description);
+ for (group_name, lint, children) in lint_groups {
+ match lint_groups_map.entry(group_name) {
+ hash_map::Entry::Vacant(entry) => {
+ entry.insert((lint, Vec::from_iter(children)));
+ }
+ hash_map::Entry::Occupied(mut entry) => {
+ // Overwrite, because some groups (such as edition incompatibility) are changed.
+ *entry.get_mut() = (lint, Vec::from_iter(children));
+ }
+ }
+ }
+ }
+
+ fn get_lints(
+ sh: &Shell,
+ edition: Edition,
+ lints_map: &mut HashMap<String, Lint>,
+ lint_groups_map: &mut HashMap<String, (Lint, Vec<String>)>,
+ lints_rustdoc_map: &mut HashMap<String, Lint>,
+ lint_groups_rustdoc_map: &mut HashMap<String, (Lint, Vec<String>)>,
+ ) {
+ let edition_str = edition.to_string();
+ let stdout = cmd!(sh, "rustdoc +nightly -Whelp -Zunstable-options --edition={edition_str}")
+ .read()
+ .unwrap();
+ let (lints, lint_groups, lints_rustdoc, lint_groups_rustdoc) = get_lints_as_text(&stdout);
+
+ insert_lints(edition, lints_map, lint_groups_map, lints, lint_groups);
+ insert_lints(
+ edition,
+ lints_rustdoc_map,
+ lint_groups_rustdoc_map,
+ lints_rustdoc,
+ lint_groups_rustdoc,
+ );
+ }
+
+ let basic_lints = cmd!(sh, "rustdoc +nightly -Whelp --edition=2015").read().unwrap();
+ let (lints, lint_groups, lints_rustdoc, lint_groups_rustdoc) = get_lints_as_text(&basic_lints);
+
+ let mut lints = lints
+ .map(|(label, description, severity)| {
+ (
+ label,
+ Lint {
+ description: description.to_owned(),
+ default_severity: severity,
+ warn_since: None,
+ deny_since: None,
+ },
+ )
+ })
+ .collect::<HashMap<_, _>>();
+ let mut lint_groups = lint_groups
+ .map(|(label, lint, children)| (label, (lint, Vec::from_iter(children))))
+ .collect::<HashMap<_, _>>();
+ let mut lints_rustdoc = lints_rustdoc
+ .map(|(label, description, severity)| {
+ (
+ label,
+ Lint {
+ description: description.to_owned(),
+ default_severity: severity,
+ warn_since: None,
+ deny_since: None,
+ },
+ )
+ })
+ .collect::<HashMap<_, _>>();
+ let mut lint_groups_rustdoc = lint_groups_rustdoc
+ .map(|(label, lint, children)| (label, (lint, Vec::from_iter(children))))
+ .collect::<HashMap<_, _>>();
+
+ for edition in Edition::iter().skip(1) {
+ get_lints(
+ sh,
+ edition,
+ &mut lints,
+ &mut lint_groups,
+ &mut lints_rustdoc,
+ &mut lint_groups_rustdoc,
+ );
+ }
+
+ let mut lints = Vec::from_iter(lints);
+ lints.sort_unstable_by(|a, b| a.0.cmp(&b.0));
+ let mut lint_groups = Vec::from_iter(lint_groups);
+ lint_groups.sort_unstable_by(|a, b| a.0.cmp(&b.0));
+ let mut lints_rustdoc = Vec::from_iter(lints_rustdoc);
+ lints_rustdoc.sort_unstable_by(|a, b| a.0.cmp(&b.0));
+ let mut lint_groups_rustdoc = Vec::from_iter(lint_groups_rustdoc);
+ lint_groups_rustdoc.sort_unstable_by(|a, b| a.0.cmp(&b.0));
+
+ buf.push_str(r#"pub const DEFAULT_LINTS: &[Lint] = &["#);
+ buf.push('\n');
+
+ for (name, lint) in &lints {
+ push_lint_completion(buf, name, lint);
+ }
+ for (name, (group, _)) in &lint_groups {
+ push_lint_completion(buf, name, group);
}
buf.push_str("];\n\n");
buf.push_str(r#"pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &["#);
- for (name, description, children) in &lints {
- if !children.is_empty() {
- // HACK: warnings is emitted with a general description, not with its members
- if name == &"warnings" {
- push_lint_group(buf, name, description, &Vec::new());
- continue;
- }
- push_lint_group(buf, &name.replace('-', "_"), description, children);
+ for (name, (lint, children)) in &lint_groups {
+ if name == "warnings" {
+ continue;
}
+ push_lint_group(buf, name, lint, children);
}
buf.push('\n');
buf.push_str("];\n");
@@ -164,37 +372,17 @@ fn generate_lint_descriptor(sh: &Shell, buf: &mut String) {
buf.push_str(r#"pub const RUSTDOC_LINTS: &[Lint] = &["#);
buf.push('\n');
- let lints_rustdoc = lints_rustdoc.lines().take_while(|l| !l.is_empty()).map(|line| {
- let (name, rest) = line.trim().split_once(char::is_whitespace).unwrap();
- let (_default_level, description) = rest.trim().split_once(char::is_whitespace).unwrap();
- (name.trim(), Cow::Borrowed(description.trim()), vec![])
- });
- let lint_groups_rustdoc =
- lint_groups_rustdoc.lines().take_while(|l| !l.is_empty()).map(|line| {
- let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap();
- (
- name.trim(),
- format!("lint group for: {}", lints.trim()).into(),
- lints
- .split_ascii_whitespace()
- .map(|s| s.trim().trim_matches(',').replace('-', "_"))
- .collect(),
- )
- });
-
- let mut lints_rustdoc = lints_rustdoc.chain(lint_groups_rustdoc).collect::<Vec<_>>();
- lints_rustdoc.sort_by(|(ident, ..), (ident2, ..)| ident.cmp(ident2));
-
- for (name, description, ..) in &lints_rustdoc {
- push_lint_completion(buf, &name.replace('-', "_"), description)
+ for (name, lint) in &lints_rustdoc {
+ push_lint_completion(buf, name, lint);
+ }
+ for (name, (group, _)) in &lint_groups_rustdoc {
+ push_lint_completion(buf, name, group);
}
buf.push_str("];\n\n");
buf.push_str(r#"pub const RUSTDOC_LINT_GROUPS: &[LintGroup] = &["#);
- for (name, description, children) in &lints_rustdoc {
- if !children.is_empty() {
- push_lint_group(buf, &name.replace('-', "_"), description, children);
- }
+ for (name, (lint, children)) in &lint_groups_rustdoc {
+ push_lint_group(buf, name, lint, children);
}
buf.push('\n');
buf.push_str("];\n");
@@ -228,13 +416,19 @@ fn generate_feature_descriptor(buf: &mut String, src_dir: &Path) {
buf.push_str(r#"pub const FEATURES: &[Lint] = &["#);
for (feature_ident, doc) in features.into_iter() {
- push_lint_completion(buf, &feature_ident, &doc)
+ let lint = Lint {
+ description: doc,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ };
+ push_lint_completion(buf, &feature_ident, &lint);
}
buf.push('\n');
buf.push_str("];\n");
}
-#[derive(Default)]
+#[derive(Debug, Default)]
struct ClippyLint {
help: String,
id: String,
@@ -295,8 +489,14 @@ fn generate_descriptor_clippy(buf: &mut String, path: &Path) {
buf.push('\n');
for clippy_lint in clippy_lints.into_iter() {
let lint_ident = format!("clippy::{}", clippy_lint.id);
- let doc = clippy_lint.help;
- push_lint_completion(buf, &lint_ident, &doc);
+ let lint = Lint {
+ description: clippy_lint.help,
+ // Allow clippy lints by default, not all users want them.
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ };
+ push_lint_completion(buf, &lint_ident, &lint);
}
buf.push_str("];\n");
@@ -306,33 +506,59 @@ fn generate_descriptor_clippy(buf: &mut String, path: &Path) {
if !children.is_empty() {
let lint_ident = format!("clippy::{id}");
let description = format!("lint group for: {}", children.join(", "));
- push_lint_group(buf, &lint_ident, &description, &children);
+ let lint = Lint {
+ description,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ };
+ push_lint_group(buf, &lint_ident, &lint, &children);
}
}
buf.push('\n');
buf.push_str("];\n");
}
-fn push_lint_completion(buf: &mut String, label: &str, description: &str) {
+fn push_lint_completion(buf: &mut String, name: &str, lint: &Lint) {
format_to!(
buf,
r###" Lint {{
label: "{}",
description: r##"{}"##,
- }},"###,
- label,
- description,
+ default_severity: {},
+ warn_since: "###,
+ name,
+ lint.description,
+ lint.default_severity,
+ );
+ match lint.warn_since {
+ Some(edition) => format_to!(buf, "Some(Edition::Edition{edition})"),
+ None => buf.push_str("None"),
+ }
+ format_to!(
+ buf,
+ r###",
+ deny_since: "###
+ );
+ match lint.deny_since {
+ Some(edition) => format_to!(buf, "Some(Edition::Edition{edition})"),
+ None => buf.push_str("None"),
+ }
+ format_to!(
+ buf,
+ r###",
+ }},"###
);
}
-fn push_lint_group(buf: &mut String, label: &str, description: &str, children: &[String]) {
+fn push_lint_group(buf: &mut String, name: &str, lint: &Lint, children: &[String]) {
buf.push_str(
r###" LintGroup {
lint:
"###,
);
- push_lint_completion(buf, label, description);
+ push_lint_completion(buf, name, lint);
let children = format!(
"&[{}]",