mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Merge pull request #18466 from ChayimFriedman2/proper-lint-severity
Properly handle different defaults for severity of lints
This commit is contained in:
commit
b707b5a117
15 changed files with 6630 additions and 1438 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -389,6 +389,10 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
|
||||
|
||||
[[package]]
|
||||
name = "edition"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
|
@ -1266,6 +1270,7 @@ name = "parser"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"drop_bomb",
|
||||
"edition",
|
||||
"expect-test",
|
||||
"limit",
|
||||
"ra-ap-rustc_lexer",
|
||||
|
@ -2662,6 +2667,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"directories",
|
||||
"edition",
|
||||
"either",
|
||||
"flate2",
|
||||
"itertools",
|
||||
|
|
|
@ -83,6 +83,7 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" }
|
|||
tt = { path = "./crates/tt", version = "0.0.0" }
|
||||
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
||||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||
edition = { path = "./crates/edition", version = "0.0.0" }
|
||||
|
||||
ra-ap-rustc_lexer = { version = "0.85", default-features = false }
|
||||
ra-ap-rustc_parse_format = { version = "0.85", default-features = false }
|
||||
|
|
13
crates/edition/Cargo.toml
Normal file
13
crates/edition/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "edition"
|
||||
version = "0.0.0"
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
|
@ -1,6 +1,5 @@
|
|||
//! The edition of the Rust language used in a crate.
|
||||
// Ideally this would be defined in the span crate, but the dependency chain is all over the place
|
||||
// wrt to span, parser and syntax.
|
||||
// This should live in a separate crate because we use it in both actual code and codegen.
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
@ -11,7 +11,7 @@ pub(super) fn complete_lint(
|
|||
existing_lints: &[ast::Path],
|
||||
lints_completions: &[Lint],
|
||||
) {
|
||||
for &Lint { label, description } in lints_completions {
|
||||
for &Lint { label, description, .. } in lints_completions {
|
||||
let (qual, name) = {
|
||||
// FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead?
|
||||
let mut parts = label.split("::");
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -327,3 +327,11 @@ impl<'a> Ranker<'a> {
|
|||
| ((no_tt_parent as usize) << 3)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Severity {
|
||||
Error,
|
||||
Warning,
|
||||
WeakWarning,
|
||||
Allow,
|
||||
}
|
||||
|
|
|
@ -586,14 +586,47 @@ fn main() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn unsafe_op_in_unsafe_fn_allowed_by_default() {
|
||||
fn unsafe_op_in_unsafe_fn_allowed_by_default_in_edition_2021() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- /lib.rs crate:foo edition:2021
|
||||
unsafe fn foo(p: *mut i32) {
|
||||
*p = 123;
|
||||
}
|
||||
"#,
|
||||
)
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- /lib.rs crate:foo edition:2021
|
||||
#![deny(warnings)]
|
||||
unsafe fn foo(p: *mut i32) {
|
||||
*p = 123;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsafe_op_in_unsafe_fn_warn_by_default_in_edition_2024() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- /lib.rs crate:foo edition:2024
|
||||
unsafe fn foo(p: *mut i32) {
|
||||
*p = 123;
|
||||
//^^💡 warn: dereference of raw pointer is unsafe and requires an unsafe function or block
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- /lib.rs crate:foo edition:2024
|
||||
#![deny(warnings)]
|
||||
unsafe fn foo(p: *mut i32) {
|
||||
*p = 123;
|
||||
//^^💡 error: dereference of raw pointer is unsafe and requires an unsafe function or block
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -84,12 +84,12 @@ use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFi
|
|||
use ide_db::{
|
||||
assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
|
||||
base_db::SourceDatabase,
|
||||
generated::lints::{LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS},
|
||||
generated::lints::{Lint, LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, DEFAULT_LINT_GROUPS},
|
||||
imports::insert_use::InsertUseConfig,
|
||||
label::Label,
|
||||
source_change::SourceChange,
|
||||
syntax_helpers::node_ext::parse_tt_as_comma_sep_paths,
|
||||
EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, SnippetCap,
|
||||
EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use syntax::{
|
||||
|
@ -210,14 +210,6 @@ impl Diagnostic {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Severity {
|
||||
Error,
|
||||
Warning,
|
||||
WeakWarning,
|
||||
Allow,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ExprFillDefaultMode {
|
||||
Todo,
|
||||
|
@ -568,26 +560,35 @@ fn handle_diag_from_macros(
|
|||
|
||||
// `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros
|
||||
|
||||
static RUSTC_LINT_GROUPS_DICT: LazyLock<FxHashMap<&str, Vec<&str>>> =
|
||||
LazyLock::new(|| build_group_dict(DEFAULT_LINT_GROUPS, &["warnings", "__RA_EVERY_LINT"], ""));
|
||||
struct BuiltLint {
|
||||
lint: &'static Lint,
|
||||
groups: Vec<&'static str>,
|
||||
}
|
||||
|
||||
static CLIPPY_LINT_GROUPS_DICT: LazyLock<FxHashMap<&str, Vec<&str>>> =
|
||||
LazyLock::new(|| build_group_dict(CLIPPY_LINT_GROUPS, &["__RA_EVERY_LINT"], "clippy::"));
|
||||
static RUSTC_LINTS: LazyLock<FxHashMap<&str, BuiltLint>> =
|
||||
LazyLock::new(|| build_lints_map(DEFAULT_LINTS, DEFAULT_LINT_GROUPS, ""));
|
||||
|
||||
static CLIPPY_LINTS: LazyLock<FxHashMap<&str, BuiltLint>> = LazyLock::new(|| {
|
||||
build_lints_map(ide_db::generated::lints::CLIPPY_LINTS, CLIPPY_LINT_GROUPS, "clippy::")
|
||||
});
|
||||
|
||||
// FIXME: Autogenerate this instead of enumerating by hand.
|
||||
static LINTS_TO_REPORT_IN_EXTERNAL_MACROS: LazyLock<FxHashSet<&str>> =
|
||||
LazyLock::new(|| FxHashSet::from_iter([]));
|
||||
|
||||
fn build_group_dict(
|
||||
fn build_lints_map(
|
||||
lints: &'static [Lint],
|
||||
lint_group: &'static [LintGroup],
|
||||
all_groups: &'static [&'static str],
|
||||
prefix: &'static str,
|
||||
) -> FxHashMap<&'static str, Vec<&'static str>> {
|
||||
let mut map_with_prefixes: FxHashMap<&str, Vec<&str>> = FxHashMap::default();
|
||||
) -> FxHashMap<&'static str, BuiltLint> {
|
||||
let mut map_with_prefixes: FxHashMap<_, _> = lints
|
||||
.iter()
|
||||
.map(|lint| (lint.label, BuiltLint { lint, groups: vec![lint.label, "__RA_EVERY_LINT"] }))
|
||||
.collect();
|
||||
for g in lint_group {
|
||||
let mut add_children = |label: &'static str| {
|
||||
for child in g.children {
|
||||
map_with_prefixes.entry(child).or_default().push(label);
|
||||
map_with_prefixes.get_mut(child).unwrap().groups.push(label);
|
||||
}
|
||||
};
|
||||
add_children(g.lint.label);
|
||||
|
@ -597,18 +598,9 @@ fn build_group_dict(
|
|||
add_children("bad_style");
|
||||
}
|
||||
}
|
||||
for (lint, groups) in map_with_prefixes.iter_mut() {
|
||||
groups.push(lint);
|
||||
groups.extend_from_slice(all_groups);
|
||||
}
|
||||
map_with_prefixes.into_iter().map(|(k, v)| (k.strip_prefix(prefix).unwrap(), v)).collect()
|
||||
}
|
||||
|
||||
/// Thd default severity for lints that are not warn by default.
|
||||
// FIXME: Autogenerate this instead of write manually.
|
||||
static LINTS_DEFAULT_SEVERITY: LazyLock<FxHashMap<&str, Severity>> =
|
||||
LazyLock::new(|| FxHashMap::from_iter([("unsafe_op_in_unsafe_fn", Severity::Allow)]));
|
||||
|
||||
fn handle_lints(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
cache: &mut FxHashMap<HirFileId, FxHashMap<SmolStr, SeverityAttr>>,
|
||||
|
@ -618,10 +610,12 @@ fn handle_lints(
|
|||
) {
|
||||
for (node, diag) in diagnostics {
|
||||
let lint = match diag.code {
|
||||
DiagnosticCode::RustcLint(lint) | DiagnosticCode::Clippy(lint) => lint,
|
||||
DiagnosticCode::RustcLint(lint) => RUSTC_LINTS[lint].lint,
|
||||
DiagnosticCode::Clippy(lint) => CLIPPY_LINTS[lint].lint,
|
||||
_ => panic!("non-lint passed to `handle_lints()`"),
|
||||
};
|
||||
if let Some(&default_severity) = LINTS_DEFAULT_SEVERITY.get(lint) {
|
||||
let default_severity = default_lint_severity(lint, edition);
|
||||
if !(default_severity == Severity::Allow && diag.severity == Severity::WeakWarning) {
|
||||
diag.severity = default_severity;
|
||||
}
|
||||
|
||||
|
@ -639,6 +633,16 @@ fn handle_lints(
|
|||
}
|
||||
}
|
||||
|
||||
fn default_lint_severity(lint: &Lint, edition: Edition) -> Severity {
|
||||
if lint.deny_since.is_some_and(|e| edition >= e) {
|
||||
Severity::Error
|
||||
} else if lint.warn_since.is_some_and(|e| edition >= e) {
|
||||
Severity::Warning
|
||||
} else {
|
||||
lint.default_severity
|
||||
}
|
||||
}
|
||||
|
||||
fn find_outline_mod_lint_severity(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
node: &InFile<SyntaxNode>,
|
||||
|
@ -654,14 +658,14 @@ fn find_outline_mod_lint_severity(
|
|||
let mod_def = sema.to_module_def(&mod_node)?;
|
||||
let module_source_file = sema.module_definition_node(mod_def);
|
||||
let mut result = None;
|
||||
let lint_groups = lint_groups(&diag.code);
|
||||
let lint_groups = lint_groups(&diag.code, edition);
|
||||
lint_attrs(
|
||||
sema,
|
||||
ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"),
|
||||
edition,
|
||||
)
|
||||
.for_each(|(lint, severity)| {
|
||||
if lint_groups.contains(&&*lint) {
|
||||
if lint_groups.contains(&lint) {
|
||||
result = Some(severity);
|
||||
}
|
||||
});
|
||||
|
@ -737,9 +741,9 @@ fn fill_lint_attrs(
|
|||
}
|
||||
});
|
||||
|
||||
let all_matching_groups = lint_groups(&diag.code)
|
||||
let all_matching_groups = lint_groups(&diag.code, edition)
|
||||
.iter()
|
||||
.filter_map(|lint_group| cached.get(&**lint_group));
|
||||
.filter_map(|lint_group| cached.get(lint_group));
|
||||
let cached_severity =
|
||||
all_matching_groups.min_by_key(|it| it.depth).map(|it| it.severity);
|
||||
|
||||
|
@ -751,7 +755,7 @@ fn fill_lint_attrs(
|
|||
// Insert this node's descendants' attributes into any outline descendant, but not including this node.
|
||||
// This must come before inserting this node's own attributes to preserve order.
|
||||
collected_lint_attrs.drain().for_each(|(lint, severity)| {
|
||||
if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) {
|
||||
if diag_severity.is_none() && lint_groups(&diag.code, edition).contains(&lint) {
|
||||
diag_severity = Some(severity.severity);
|
||||
}
|
||||
|
||||
|
@ -774,7 +778,7 @@ fn fill_lint_attrs(
|
|||
if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) {
|
||||
// Insert this node's attributes into any outline descendant, including this node.
|
||||
lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| {
|
||||
if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) {
|
||||
if diag_severity.is_none() && lint_groups(&diag.code, edition).contains(&lint) {
|
||||
diag_severity = Some(severity);
|
||||
}
|
||||
|
||||
|
@ -804,7 +808,7 @@ fn fill_lint_attrs(
|
|||
return diag_severity;
|
||||
} else if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) {
|
||||
lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| {
|
||||
if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) {
|
||||
if diag_severity.is_none() && lint_groups(&diag.code, edition).contains(&lint) {
|
||||
diag_severity = Some(severity);
|
||||
}
|
||||
|
||||
|
@ -905,16 +909,37 @@ fn cfg_attr_lint_attrs(
|
|||
}
|
||||
}
|
||||
|
||||
fn lint_groups(lint: &DiagnosticCode) -> &'static [&'static str] {
|
||||
match lint {
|
||||
#[derive(Debug)]
|
||||
struct LintGroups {
|
||||
groups: &'static [&'static str],
|
||||
inside_warnings: bool,
|
||||
}
|
||||
|
||||
impl LintGroups {
|
||||
fn contains(&self, group: &str) -> bool {
|
||||
self.groups.contains(&group) || (self.inside_warnings && group == "warnings")
|
||||
}
|
||||
|
||||
fn iter(&self) -> impl Iterator<Item = &'static str> {
|
||||
self.groups.iter().copied().chain(self.inside_warnings.then_some("warnings"))
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_groups(lint: &DiagnosticCode, edition: Edition) -> LintGroups {
|
||||
let (groups, inside_warnings) = match lint {
|
||||
DiagnosticCode::RustcLint(name) => {
|
||||
RUSTC_LINT_GROUPS_DICT.get(name).map(|it| &**it).unwrap_or_default()
|
||||
let lint = &RUSTC_LINTS[name];
|
||||
let inside_warnings = default_lint_severity(lint.lint, edition) == Severity::Warning;
|
||||
(&lint.groups, inside_warnings)
|
||||
}
|
||||
DiagnosticCode::Clippy(name) => {
|
||||
CLIPPY_LINT_GROUPS_DICT.get(name).map(|it| &**it).unwrap_or_default()
|
||||
let lint = &CLIPPY_LINTS[name];
|
||||
let inside_warnings = default_lint_severity(lint.lint, edition) == Severity::Warning;
|
||||
(&lint.groups, inside_warnings)
|
||||
}
|
||||
_ => &[],
|
||||
}
|
||||
_ => panic!("non-lint passed to `handle_lints()`"),
|
||||
};
|
||||
LintGroups { groups, inside_warnings }
|
||||
}
|
||||
|
||||
fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
|
||||
|
|
|
@ -6413,7 +6413,7 @@ fn hover_feature() {
|
|||
by the codegen backend, but not the MIR inliner.
|
||||
|
||||
```rust
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(intrinsics)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
#[rustc_intrinsic]
|
||||
|
@ -6423,7 +6423,7 @@ fn hover_feature() {
|
|||
Since these are just regular functions, it is perfectly ok to create the intrinsic twice:
|
||||
|
||||
```rust
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(intrinsics)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
#[rustc_intrinsic]
|
||||
|
|
|
@ -132,11 +132,9 @@ pub use ide_db::{
|
|||
search::{ReferenceCategory, SearchScope},
|
||||
source_change::{FileSystemEdit, SnippetEdit, SourceChange},
|
||||
symbol_index::Query,
|
||||
FileId, FilePosition, FileRange, RootDatabase, SymbolKind,
|
||||
};
|
||||
pub use ide_diagnostics::{
|
||||
Diagnostic, DiagnosticCode, DiagnosticsConfig, ExprFillDefaultMode, Severity,
|
||||
FileId, FilePosition, FileRange, RootDatabase, Severity, SymbolKind,
|
||||
};
|
||||
pub use ide_diagnostics::{Diagnostic, DiagnosticCode, DiagnosticsConfig, ExprFillDefaultMode};
|
||||
pub use ide_ssr::SsrError;
|
||||
pub use span::Edition;
|
||||
pub use syntax::{TextRange, TextSize};
|
||||
|
|
|
@ -18,6 +18,8 @@ ra-ap-rustc_lexer.workspace = true
|
|||
limit.workspace = true
|
||||
tracing = { workspace = true, optional = true }
|
||||
|
||||
edition.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.0"
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ extern crate ra_ap_rustc_lexer as rustc_lexer;
|
|||
#[cfg(feature = "in-rust-tree")]
|
||||
extern crate rustc_lexer;
|
||||
|
||||
mod edition;
|
||||
mod event;
|
||||
mod grammar;
|
||||
mod input;
|
||||
|
@ -41,8 +40,9 @@ mod tests;
|
|||
|
||||
pub(crate) use token_set::TokenSet;
|
||||
|
||||
pub use edition::Edition;
|
||||
|
||||
pub use crate::{
|
||||
edition::Edition,
|
||||
input::Input,
|
||||
lexed_str::LexedStr,
|
||||
output::{Output, Step},
|
||||
|
|
|
@ -21,6 +21,7 @@ quote = "1.0.20"
|
|||
ungrammar = "1.16.1"
|
||||
either.workspace = true
|
||||
itertools.workspace = true
|
||||
edition.workspace = true
|
||||
# Avoid adding more dependencies to this crate
|
||||
|
||||
[lints]
|
||||
|
|
|
@ -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)
|
||||
});
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
(lints, lint_groups, lints_rustdoc, lint_groups_rustdoc)
|
||||
}
|
||||
|
||||
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 (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');
|
||||
|
||||
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(),
|
||||
)
|
||||
});
|
||||
|
||||
let mut lints = lints.chain(lint_groups).collect::<Vec<_>>();
|
||||
lints.sort_by(|(ident, ..), (ident2, ..)| ident.cmp(ident2));
|
||||
|
||||
for (name, description, ..) in &lints {
|
||||
push_lint_completion(buf, &name.replace('-', "_"), description);
|
||||
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!(
|
||||
"&[{}]",
|
||||
|
|
Loading…
Reference in a new issue