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