Auto merge of #10751 - blyxyas:explain_with_config, r=xFrednet
Add configuration options to `--explain` This PR rearranges some modules, taking `metadata_collector` out of `internal_lints` and making public just the necessary functions for `explain()` to use. The output looks something like this: ```sh $ cargo run --bin cargo-clippy --manifest-path ../rust-clippy/Cargo.toml -- --explain cognitive_complexity ### What it does Checks for methods with high cognitive complexity. ### Why is this bad? Methods of high cognitive complexity tend to be hard to both read and maintain. Also LLVM will tend to optimize small methods better. ### Known problems Sometimes it's hard to find a way to reduce the complexity. ### Example You'll see it when you get the warning. ======================================== Configuration for clippy::cognitive_complexity: - cognitive-complexity-threshold: The maximum cognitive complexity a function can have (default: 25) ``` Fixes #9990 r? `@xFrednet` --- changelog: Docs: `cargo clippy --explain LINT` now shows possible configuration options for the explained lint [#10751](https://github.com/rust-lang/rust-clippy/pull/10751) <!-- changelog_checked -->
This commit is contained in:
commit
1407c7627e
@ -332,8 +332,11 @@
|
|||||||
mod zero_sized_map_values;
|
mod zero_sized_map_values;
|
||||||
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
||||||
|
|
||||||
use crate::utils::conf::{format_error, TryConf};
|
|
||||||
pub use crate::utils::conf::{lookup_conf_file, Conf};
|
pub use crate::utils::conf::{lookup_conf_file, Conf};
|
||||||
|
use crate::utils::{
|
||||||
|
conf::{format_error, metadata::get_configuration_metadata, TryConf},
|
||||||
|
FindAll,
|
||||||
|
};
|
||||||
|
|
||||||
/// Register all pre expansion lints
|
/// Register all pre expansion lints
|
||||||
///
|
///
|
||||||
@ -389,7 +392,7 @@ pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>
|
|||||||
conf
|
conf
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)] //~ ERROR no such field
|
||||||
struct RegistrationGroups {
|
struct RegistrationGroups {
|
||||||
all: Vec<LintId>,
|
all: Vec<LintId>,
|
||||||
cargo: Vec<LintId>,
|
cargo: Vec<LintId>,
|
||||||
@ -472,7 +475,22 @@ pub(crate) struct LintInfo {
|
|||||||
pub fn explain(name: &str) {
|
pub fn explain(name: &str) {
|
||||||
let target = format!("clippy::{}", name.to_ascii_uppercase());
|
let target = format!("clippy::{}", name.to_ascii_uppercase());
|
||||||
match declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
|
match declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
|
||||||
Some(info) => print!("{}", info.explanation),
|
Some(info) => {
|
||||||
|
println!("{}", info.explanation);
|
||||||
|
// Check if the lint has configuration
|
||||||
|
let mdconf = get_configuration_metadata();
|
||||||
|
if let Some(config_vec_positions) = mdconf
|
||||||
|
.iter()
|
||||||
|
.find_all(|cconf| cconf.lints.contains(&info.lint.name_lower()[8..].to_owned()))
|
||||||
|
{
|
||||||
|
// If it has, print it
|
||||||
|
println!("### Configuration for {}:\n", info.lint.name_lower());
|
||||||
|
for position in config_vec_positions {
|
||||||
|
let conf = &mdconf[position];
|
||||||
|
println!(" - {}: {} (default: {})", conf.name, conf.doc, conf.default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
None => println!("unknown lint: {name}"),
|
None => println!("unknown lint: {name}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,16 +174,15 @@ fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "internal")]
|
|
||||||
pub mod metadata {
|
pub mod metadata {
|
||||||
use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
|
use crate::utils::ClippyConfiguration;
|
||||||
|
|
||||||
macro_rules! wrap_option {
|
macro_rules! wrap_option {
|
||||||
() => (None);
|
() => (None);
|
||||||
($x:literal) => (Some($x));
|
($x:literal) => (Some($x));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
|
pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
|
||||||
vec![
|
vec![
|
||||||
$(
|
$(
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,11 @@
|
|||||||
//! a simple mistake)
|
//! a simple mistake)
|
||||||
|
|
||||||
use crate::renamed_lints::RENAMED_LINTS;
|
use crate::renamed_lints::RENAMED_LINTS;
|
||||||
use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type};
|
use crate::utils::{
|
||||||
|
collect_configs,
|
||||||
|
internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type},
|
||||||
|
ClippyConfiguration,
|
||||||
|
};
|
||||||
|
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
|
use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
|
||||||
@ -520,111 +524,6 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================================================================
|
|
||||||
// Configuration
|
|
||||||
// ==================================================================
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct ClippyConfiguration {
|
|
||||||
name: String,
|
|
||||||
config_type: &'static str,
|
|
||||||
default: String,
|
|
||||||
lints: Vec<String>,
|
|
||||||
doc: String,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
deprecation_reason: Option<&'static str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClippyConfiguration {
|
|
||||||
pub fn new(
|
|
||||||
name: &'static str,
|
|
||||||
config_type: &'static str,
|
|
||||||
default: String,
|
|
||||||
doc_comment: &'static str,
|
|
||||||
deprecation_reason: Option<&'static str>,
|
|
||||||
) -> Self {
|
|
||||||
let (lints, doc) = parse_config_field_doc(doc_comment)
|
|
||||||
.unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
name: to_kebab(name),
|
|
||||||
lints,
|
|
||||||
doc,
|
|
||||||
config_type,
|
|
||||||
default,
|
|
||||||
deprecation_reason,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_markdown_paragraph(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n",
|
|
||||||
self.name,
|
|
||||||
self.doc
|
|
||||||
.lines()
|
|
||||||
.map(|line| line.strip_prefix(" ").unwrap_or(line))
|
|
||||||
.join("\n"),
|
|
||||||
self.default,
|
|
||||||
self.config_type,
|
|
||||||
self.lints
|
|
||||||
.iter()
|
|
||||||
.map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
|
|
||||||
.map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
|
|
||||||
.join("\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_markdown_table_entry(&self) -> String {
|
|
||||||
format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_configs() -> Vec<ClippyConfiguration> {
|
|
||||||
crate::utils::conf::metadata::get_configuration_metadata()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This parses the field documentation of the config struct.
|
|
||||||
///
|
|
||||||
/// ```rust, ignore
|
|
||||||
/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Would yield:
|
|
||||||
/// ```rust, ignore
|
|
||||||
/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
|
|
||||||
/// ```
|
|
||||||
fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
|
|
||||||
const DOC_START: &str = " Lint: ";
|
|
||||||
if_chain! {
|
|
||||||
if doc_comment.starts_with(DOC_START);
|
|
||||||
if let Some(split_pos) = doc_comment.find('.');
|
|
||||||
then {
|
|
||||||
let mut doc_comment = doc_comment.to_string();
|
|
||||||
let mut documentation = doc_comment.split_off(split_pos);
|
|
||||||
|
|
||||||
// Extract lints
|
|
||||||
doc_comment.make_ascii_lowercase();
|
|
||||||
let lints: Vec<String> = doc_comment
|
|
||||||
.split_off(DOC_START.len())
|
|
||||||
.split(", ")
|
|
||||||
.map(str::to_string)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Format documentation correctly
|
|
||||||
// split off leading `.` from lint name list and indent for correct formatting
|
|
||||||
documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n ");
|
|
||||||
|
|
||||||
Some((lints, documentation))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
|
|
||||||
fn to_kebab(config_name: &str) -> String {
|
|
||||||
config_name.replace('_', "-")
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ClippyConfiguration {
|
impl fmt::Display for ClippyConfiguration {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
||||||
writeln!(
|
writeln!(
|
||||||
|
@ -4,3 +4,143 @@
|
|||||||
pub mod format_args_collector;
|
pub mod format_args_collector;
|
||||||
#[cfg(feature = "internal")]
|
#[cfg(feature = "internal")]
|
||||||
pub mod internal_lints;
|
pub mod internal_lints;
|
||||||
|
#[cfg(feature = "internal")]
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
|
||||||
|
fn to_kebab(config_name: &str) -> String {
|
||||||
|
config_name.replace('_', "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
// Configuration
|
||||||
|
// ==================================================================
|
||||||
|
#[derive(Debug, Clone, Default)] //~ ERROR no such field
|
||||||
|
pub struct ClippyConfiguration {
|
||||||
|
pub name: String,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
config_type: &'static str,
|
||||||
|
pub default: String,
|
||||||
|
pub lints: Vec<String>,
|
||||||
|
pub doc: String,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
deprecation_reason: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClippyConfiguration {
|
||||||
|
pub fn new(
|
||||||
|
name: &'static str,
|
||||||
|
config_type: &'static str,
|
||||||
|
default: String,
|
||||||
|
doc_comment: &'static str,
|
||||||
|
deprecation_reason: Option<&'static str>,
|
||||||
|
) -> Self {
|
||||||
|
let (lints, doc) = parse_config_field_doc(doc_comment)
|
||||||
|
.unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
name: to_kebab(name),
|
||||||
|
lints,
|
||||||
|
doc,
|
||||||
|
config_type,
|
||||||
|
default,
|
||||||
|
deprecation_reason,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "internal")]
|
||||||
|
fn to_markdown_paragraph(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n",
|
||||||
|
self.name,
|
||||||
|
self.doc
|
||||||
|
.lines()
|
||||||
|
.map(|line| line.strip_prefix(" ").unwrap_or(line))
|
||||||
|
.join("\n"),
|
||||||
|
self.default,
|
||||||
|
self.config_type,
|
||||||
|
self.lints
|
||||||
|
.iter()
|
||||||
|
.map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
|
||||||
|
.map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
|
||||||
|
.join("\n"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "internal")]
|
||||||
|
fn to_markdown_table_entry(&self) -> String {
|
||||||
|
format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "internal")]
|
||||||
|
fn collect_configs() -> Vec<ClippyConfiguration> {
|
||||||
|
crate::utils::conf::metadata::get_configuration_metadata()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This parses the field documentation of the config struct.
|
||||||
|
///
|
||||||
|
/// ```rust, ignore
|
||||||
|
/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Would yield:
|
||||||
|
/// ```rust, ignore
|
||||||
|
/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
|
||||||
|
/// ```
|
||||||
|
fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
|
||||||
|
const DOC_START: &str = " Lint: ";
|
||||||
|
if_chain! {
|
||||||
|
if doc_comment.starts_with(DOC_START);
|
||||||
|
if let Some(split_pos) = doc_comment.find('.');
|
||||||
|
then {
|
||||||
|
let mut doc_comment = doc_comment.to_string();
|
||||||
|
let mut documentation = doc_comment.split_off(split_pos);
|
||||||
|
|
||||||
|
// Extract lints
|
||||||
|
doc_comment.make_ascii_lowercase();
|
||||||
|
let lints: Vec<String> = doc_comment
|
||||||
|
.split_off(DOC_START.len())
|
||||||
|
.split(", ")
|
||||||
|
.map(str::to_string)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Format documentation correctly
|
||||||
|
// split off leading `.` from lint name list and indent for correct formatting
|
||||||
|
documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n ");
|
||||||
|
|
||||||
|
Some((lints, documentation))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shamelessly stolen from find_all (https://github.com/nectariner/find_all)
|
||||||
|
pub trait FindAll: Iterator + Sized {
|
||||||
|
fn find_all<P>(&mut self, predicate: P) -> Option<Vec<usize>>
|
||||||
|
where
|
||||||
|
P: FnMut(&Self::Item) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> FindAll for I
|
||||||
|
where
|
||||||
|
I: Iterator,
|
||||||
|
{
|
||||||
|
fn find_all<P>(&mut self, mut predicate: P) -> Option<Vec<usize>>
|
||||||
|
where
|
||||||
|
P: FnMut(&Self::Item) -> bool,
|
||||||
|
{
|
||||||
|
let mut occurences = Vec::<usize>::default();
|
||||||
|
for (index, element) in self.enumerate() {
|
||||||
|
if predicate(&element) {
|
||||||
|
occurences.push(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match occurences.len() {
|
||||||
|
0 => None,
|
||||||
|
_ => Some(occurences),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user