Skip to content

Commit 1407c76

Browse files
committed
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](#10751) <!-- changelog_checked -->
2 parents 77e4d7a + 3e8fea6 commit 1407c76

File tree

4 files changed

+168
-112
lines changed

4 files changed

+168
-112
lines changed

Diff for: clippy_lints/src/lib.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,11 @@ mod zero_div_zero;
332332
mod zero_sized_map_values;
333333
// end lints modules, do not remove this comment, it’s used in `update_lints`
334334

335-
use crate::utils::conf::{format_error, TryConf};
336335
pub use crate::utils::conf::{lookup_conf_file, Conf};
336+
use crate::utils::{
337+
conf::{format_error, metadata::get_configuration_metadata, TryConf},
338+
FindAll,
339+
};
337340

338341
/// Register all pre expansion lints
339342
///
@@ -389,7 +392,7 @@ pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>
389392
conf
390393
}
391394

392-
#[derive(Default)]
395+
#[derive(Default)] //~ ERROR no such field
393396
struct RegistrationGroups {
394397
all: Vec<LintId>,
395398
cargo: Vec<LintId>,
@@ -472,7 +475,22 @@ pub(crate) struct LintInfo {
472475
pub fn explain(name: &str) {
473476
let target = format!("clippy::{}", name.to_ascii_uppercase());
474477
match declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
475-
Some(info) => print!("{}", info.explanation),
478+
Some(info) => {
479+
println!("{}", info.explanation);
480+
// Check if the lint has configuration
481+
let mdconf = get_configuration_metadata();
482+
if let Some(config_vec_positions) = mdconf
483+
.iter()
484+
.find_all(|cconf| cconf.lints.contains(&info.lint.name_lower()[8..].to_owned()))
485+
{
486+
// If it has, print it
487+
println!("### Configuration for {}:\n", info.lint.name_lower());
488+
for position in config_vec_positions {
489+
let conf = &mdconf[position];
490+
println!(" - {}: {} (default: {})", conf.name, conf.doc, conf.default);
491+
}
492+
}
493+
},
476494
None => println!("unknown lint: {name}"),
477495
}
478496
}

Diff for: clippy_lints/src/utils/conf.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,15 @@ macro_rules! define_Conf {
174174
}
175175
}
176176

177-
#[cfg(feature = "internal")]
178177
pub mod metadata {
179-
use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
178+
use crate::utils::ClippyConfiguration;
180179

181180
macro_rules! wrap_option {
182181
() => (None);
183182
($x:literal) => (Some($x));
184183
}
185184

186-
pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
185+
pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
187186
vec![
188187
$(
189188
{

Diff for: clippy_lints/src/utils/internal_lints/metadata_collector.rs

+5-106
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
//! a simple mistake)
99
1010
use crate::renamed_lints::RENAMED_LINTS;
11-
use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type};
11+
use crate::utils::{
12+
collect_configs,
13+
internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type},
14+
ClippyConfiguration,
15+
};
1216

1317
use clippy_utils::diagnostics::span_lint;
1418
use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
@@ -520,111 +524,6 @@ impl Serialize for ApplicabilityInfo {
520524
}
521525
}
522526

523-
// ==================================================================
524-
// Configuration
525-
// ==================================================================
526-
#[derive(Debug, Clone, Default)]
527-
pub struct ClippyConfiguration {
528-
name: String,
529-
config_type: &'static str,
530-
default: String,
531-
lints: Vec<String>,
532-
doc: String,
533-
#[allow(dead_code)]
534-
deprecation_reason: Option<&'static str>,
535-
}
536-
537-
impl ClippyConfiguration {
538-
pub fn new(
539-
name: &'static str,
540-
config_type: &'static str,
541-
default: String,
542-
doc_comment: &'static str,
543-
deprecation_reason: Option<&'static str>,
544-
) -> Self {
545-
let (lints, doc) = parse_config_field_doc(doc_comment)
546-
.unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
547-
548-
Self {
549-
name: to_kebab(name),
550-
lints,
551-
doc,
552-
config_type,
553-
default,
554-
deprecation_reason,
555-
}
556-
}
557-
558-
fn to_markdown_paragraph(&self) -> String {
559-
format!(
560-
"### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n",
561-
self.name,
562-
self.doc
563-
.lines()
564-
.map(|line| line.strip_prefix(" ").unwrap_or(line))
565-
.join("\n"),
566-
self.default,
567-
self.config_type,
568-
self.lints
569-
.iter()
570-
.map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
571-
.map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
572-
.join("\n"),
573-
)
574-
}
575-
576-
fn to_markdown_table_entry(&self) -> String {
577-
format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default)
578-
}
579-
}
580-
581-
fn collect_configs() -> Vec<ClippyConfiguration> {
582-
crate::utils::conf::metadata::get_configuration_metadata()
583-
}
584-
585-
/// This parses the field documentation of the config struct.
586-
///
587-
/// ```rust, ignore
588-
/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
589-
/// ```
590-
///
591-
/// Would yield:
592-
/// ```rust, ignore
593-
/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
594-
/// ```
595-
fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
596-
const DOC_START: &str = " Lint: ";
597-
if_chain! {
598-
if doc_comment.starts_with(DOC_START);
599-
if let Some(split_pos) = doc_comment.find('.');
600-
then {
601-
let mut doc_comment = doc_comment.to_string();
602-
let mut documentation = doc_comment.split_off(split_pos);
603-
604-
// Extract lints
605-
doc_comment.make_ascii_lowercase();
606-
let lints: Vec<String> = doc_comment
607-
.split_off(DOC_START.len())
608-
.split(", ")
609-
.map(str::to_string)
610-
.collect();
611-
612-
// Format documentation correctly
613-
// split off leading `.` from lint name list and indent for correct formatting
614-
documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n ");
615-
616-
Some((lints, documentation))
617-
} else {
618-
None
619-
}
620-
}
621-
}
622-
623-
/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
624-
fn to_kebab(config_name: &str) -> String {
625-
config_name.replace('_', "-")
626-
}
627-
628527
impl fmt::Display for ClippyConfiguration {
629528
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
630529
writeln!(

Diff for: clippy_lints/src/utils/mod.rs

+140
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,143 @@ pub mod dump_hir;
44
pub mod format_args_collector;
55
#[cfg(feature = "internal")]
66
pub mod internal_lints;
7+
#[cfg(feature = "internal")]
8+
use itertools::Itertools;
9+
10+
/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
11+
fn to_kebab(config_name: &str) -> String {
12+
config_name.replace('_', "-")
13+
}
14+
15+
// ==================================================================
16+
// Configuration
17+
// ==================================================================
18+
#[derive(Debug, Clone, Default)] //~ ERROR no such field
19+
pub struct ClippyConfiguration {
20+
pub name: String,
21+
#[allow(dead_code)]
22+
config_type: &'static str,
23+
pub default: String,
24+
pub lints: Vec<String>,
25+
pub doc: String,
26+
#[allow(dead_code)]
27+
deprecation_reason: Option<&'static str>,
28+
}
29+
30+
impl ClippyConfiguration {
31+
pub fn new(
32+
name: &'static str,
33+
config_type: &'static str,
34+
default: String,
35+
doc_comment: &'static str,
36+
deprecation_reason: Option<&'static str>,
37+
) -> Self {
38+
let (lints, doc) = parse_config_field_doc(doc_comment)
39+
.unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
40+
41+
Self {
42+
name: to_kebab(name),
43+
lints,
44+
doc,
45+
config_type,
46+
default,
47+
deprecation_reason,
48+
}
49+
}
50+
51+
#[cfg(feature = "internal")]
52+
fn to_markdown_paragraph(&self) -> String {
53+
format!(
54+
"### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n",
55+
self.name,
56+
self.doc
57+
.lines()
58+
.map(|line| line.strip_prefix(" ").unwrap_or(line))
59+
.join("\n"),
60+
self.default,
61+
self.config_type,
62+
self.lints
63+
.iter()
64+
.map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
65+
.map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
66+
.join("\n"),
67+
)
68+
}
69+
70+
#[cfg(feature = "internal")]
71+
fn to_markdown_table_entry(&self) -> String {
72+
format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default)
73+
}
74+
}
75+
76+
#[cfg(feature = "internal")]
77+
fn collect_configs() -> Vec<ClippyConfiguration> {
78+
crate::utils::conf::metadata::get_configuration_metadata()
79+
}
80+
81+
/// This parses the field documentation of the config struct.
82+
///
83+
/// ```rust, ignore
84+
/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
85+
/// ```
86+
///
87+
/// Would yield:
88+
/// ```rust, ignore
89+
/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
90+
/// ```
91+
fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
92+
const DOC_START: &str = " Lint: ";
93+
if_chain! {
94+
if doc_comment.starts_with(DOC_START);
95+
if let Some(split_pos) = doc_comment.find('.');
96+
then {
97+
let mut doc_comment = doc_comment.to_string();
98+
let mut documentation = doc_comment.split_off(split_pos);
99+
100+
// Extract lints
101+
doc_comment.make_ascii_lowercase();
102+
let lints: Vec<String> = doc_comment
103+
.split_off(DOC_START.len())
104+
.split(", ")
105+
.map(str::to_string)
106+
.collect();
107+
108+
// Format documentation correctly
109+
// split off leading `.` from lint name list and indent for correct formatting
110+
documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n ");
111+
112+
Some((lints, documentation))
113+
} else {
114+
None
115+
}
116+
}
117+
}
118+
119+
// Shamelessly stolen from find_all (https://github.com/nectariner/find_all)
120+
pub trait FindAll: Iterator + Sized {
121+
fn find_all<P>(&mut self, predicate: P) -> Option<Vec<usize>>
122+
where
123+
P: FnMut(&Self::Item) -> bool;
124+
}
125+
126+
impl<I> FindAll for I
127+
where
128+
I: Iterator,
129+
{
130+
fn find_all<P>(&mut self, mut predicate: P) -> Option<Vec<usize>>
131+
where
132+
P: FnMut(&Self::Item) -> bool,
133+
{
134+
let mut occurences = Vec::<usize>::default();
135+
for (index, element) in self.enumerate() {
136+
if predicate(&element) {
137+
occurences.push(index);
138+
}
139+
}
140+
141+
match occurences.len() {
142+
0 => None,
143+
_ => Some(occurences),
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)