| pub mod author; |
| pub mod conf; |
| pub mod dump_hir; |
| pub mod format_args_collector; |
| #[cfg(feature = "internal")] |
| 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('_', "-") |
| } |
| |
| #[cfg(feature = "internal")] |
| const BOOK_CONFIGS_PATH: &str = "https://doc.rust-lang.org/clippy/lint_configuration.html"; |
| |
| // ================================================================== |
| // Configuration |
| // ================================================================== |
| #[derive(Debug, Clone, Default)] |
| 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**Affected lints:**\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_link(&self) -> String { |
| format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name) |
| } |
| } |
| |
| #[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), |
| } |
| } |
| } |