• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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