+use core::fmt::Write;
use itertools::Itertools;
-use regex::Regex;
+use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs;
-use std::lazy::SyncLazy;
use std::path::Path;
use walkdir::WalkDir;
// Use that command to update this file and do not edit by hand.\n\
// Manual edits will be overwritten.\n\n";
-static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
- Regex::new(
- r#"(?x)
- declare_clippy_lint!\s*[\{(]
- (?:\s+///.*)*
- (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
- \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
- (?P<cat>[a-z_]+)\s*,\s*
- "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
-"#,
- )
- .unwrap()
-});
-
-static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
- Regex::new(
- r#"(?x)
- declare_deprecated_lint!\s*[{(]\s*
- (?:\s+///.*)*
- (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
- \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
- "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
-"#,
- )
- .unwrap()
-});
-static NL_ESCAPE_RE: SyncLazy<Regex> = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap());
-
-static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
+const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
#[derive(Clone, Copy, PartialEq)]
pub enum UpdateMode {
/// Panics if a file path could not read from or then written to
#[allow(clippy::too_many_lines)]
pub fn run(update_mode: UpdateMode) {
- let lint_list: Vec<Lint> = gather_all().collect();
+ let (lints, deprecated_lints) = gather_all();
- let internal_lints = Lint::internal_lints(&lint_list);
- let deprecated_lints = Lint::deprecated_lints(&lint_list);
- let usable_lints = Lint::usable_lints(&lint_list);
+ let internal_lints = Lint::internal_lints(&lints);
+ let usable_lints = Lint::usable_lints(&lints);
let mut sorted_usable_lints = usable_lints.clone();
sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
- let usable_lint_count = round_to_fifty(usable_lints.len());
-
- let mut file_change = false;
-
- file_change |= replace_region_in_file(
+ replace_region_in_file(
+ update_mode,
Path::new("README.md"),
- &format!(
- r#"\[There are over \d+ lints included in this crate!\]\({}\)"#,
- DOCS_LINK
- ),
- "",
- true,
- update_mode == UpdateMode::Change,
- || {
- vec![format!(
- "[There are over {} lints included in this crate!]({})",
- usable_lint_count, DOCS_LINK
- )]
+ "[There are over ",
+ " lints included in this crate!]",
+ |res| {
+ write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
},
- )
- .changed;
+ );
- file_change |= replace_region_in_file(
+ replace_region_in_file(
+ update_mode,
Path::new("CHANGELOG.md"),
- "<!-- begin autogenerated links to lint list -->",
+ "<!-- begin autogenerated links to lint list -->\n",
"<!-- end autogenerated links to lint list -->",
- false,
- update_mode == UpdateMode::Change,
- || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())),
- )
- .changed;
+ |res| {
+ for lint in usable_lints
+ .iter()
+ .map(|l| &l.name)
+ .chain(deprecated_lints.iter().map(|l| &l.name))
+ .sorted()
+ {
+ writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap();
+ }
+ },
+ );
// This has to be in lib.rs, otherwise rustfmt doesn't work
- file_change |= replace_region_in_file(
+ replace_region_in_file(
+ update_mode,
Path::new("clippy_lints/src/lib.rs"),
- "begin lints modules",
- "end lints modules",
- false,
- update_mode == UpdateMode::Change,
- || gen_modules_list(usable_lints.iter()),
- )
- .changed;
-
- if file_change && update_mode == UpdateMode::Check {
- exit_with_failure();
- }
+ "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
+ "// end lints modules, do not remove this comment, it’s used in `update_lints`",
+ |res| {
+ for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
+ writeln!(res, "mod {};", lint_mod).unwrap();
+ }
+ },
+ );
process_file(
"clippy_lints/src/lib.register_lints.rs",
process_file(
"clippy_lints/src/lib.deprecated.rs",
update_mode,
- &gen_deprecated(deprecated_lints.iter()),
+ &gen_deprecated(&deprecated_lints),
);
let all_group_lints = usable_lints.iter().filter(|l| {
}
pub fn print_lints() {
- let lint_list: Vec<Lint> = gather_all().collect();
+ let (lint_list, _) = gather_all();
let usable_lints = Lint::usable_lints(&lint_list);
let usable_lint_count = usable_lints.len();
let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
for (lint_group, mut lints) in grouped_by_lint_group {
- if lint_group == "Deprecated" {
- continue;
- }
println!("\n## {}", lint_group);
lints.sort_by_key(|l| l.name.clone());
name: String,
group: String,
desc: String,
- deprecation: Option<String>,
module: String,
}
impl Lint {
#[must_use]
- fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
+ fn new(name: &str, group: &str, desc: &str, module: &str) -> Self {
Self {
name: name.to_lowercase(),
- group: group.to_string(),
- desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
- deprecation: deprecation.map(ToString::to_string),
- module: module.to_string(),
+ group: group.into(),
+ desc: remove_line_splices(desc),
+ module: module.into(),
}
}
fn usable_lints(lints: &[Self]) -> Vec<Self> {
lints
.iter()
- .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
+ .filter(|l| !l.group.starts_with("internal"))
.cloned()
.collect()
}
lints.iter().filter(|l| l.group == "internal").cloned().collect()
}
- /// Returns all deprecated lints
- #[must_use]
- fn deprecated_lints(lints: &[Self]) -> Vec<Self> {
- lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
- }
-
/// Returns the lints in a `HashMap`, grouped by the different lint groups
#[must_use]
fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
}
}
+#[derive(Clone, PartialEq, Debug)]
+struct DeprecatedLint {
+ name: String,
+ reason: String,
+}
+impl DeprecatedLint {
+ fn new(name: &str, reason: &str) -> Self {
+ Self {
+ name: name.to_lowercase(),
+ reason: remove_line_splices(reason),
+ }
+ }
+}
+
/// Generates the code for registering a group
fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
output
}
-/// Generates the module declarations for `lints`
-#[must_use]
-fn gen_modules_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
- lints
- .map(|l| &l.module)
- .unique()
- .map(|module| format!("mod {};", module))
- .sorted()
- .collect::<Vec<String>>()
-}
-
-/// Generates the list of lint links at the bottom of the CHANGELOG
-#[must_use]
-fn gen_changelog_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
- lints
- .sorted_by_key(|l| &l.name)
- .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
- .collect()
-}
-
/// Generates the `register_removed` code
#[must_use]
-fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> String {
+fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
let mut output = GENERATED_FILE_COMMENT.to_string();
output.push_str("{\n");
- for Lint { name, deprecation, .. } in lints {
+ for lint in lints {
output.push_str(&format!(
concat!(
" store.register_removed(\n",
" \"{}\",\n",
" );\n"
),
- name,
- deprecation.as_ref().expect("`lints` are deprecated")
+ lint.name, lint.reason,
));
}
output.push_str("}\n");
output
}
-/// Gathers all files in `src/clippy_lints` and gathers all lints inside
-fn gather_all() -> impl Iterator<Item = Lint> {
- lint_files().flat_map(|f| gather_from_file(&f))
-}
+/// Gathers all lints defined in `clippy_lints/src`
+fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>) {
+ let mut lints = Vec::with_capacity(1000);
+ let mut deprecated_lints = Vec::with_capacity(50);
+ let root_path = clippy_project_root().join("clippy_lints/src");
-fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item = Lint> {
- let content = fs::read_to_string(dir_entry.path()).unwrap();
- let path = dir_entry.path();
- let filename = path.file_stem().unwrap();
- let path_buf = path.with_file_name(filename);
- let mut rel_path = path_buf
- .strip_prefix(clippy_project_root().join("clippy_lints/src"))
- .expect("only files in `clippy_lints/src` should be looked at");
- // If the lints are stored in mod.rs, we get the module name from
- // the containing directory:
- if filename == "mod" {
- rel_path = rel_path.parent().unwrap();
- }
+ for (rel_path, file) in WalkDir::new(&root_path)
+ .into_iter()
+ .map(Result::unwrap)
+ .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+ .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
+ {
+ let path = file.path();
+ let contents =
+ fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
+ let module = rel_path
+ .components()
+ .map(|c| c.as_os_str().to_str().unwrap())
+ .collect::<Vec<_>>()
+ .join("::");
+
+ // If the lints are stored in mod.rs, we get the module name from
+ // the containing directory:
+ let module = if let Some(module) = module.strip_suffix("::mod.rs") {
+ module
+ } else {
+ module.strip_suffix(".rs").unwrap_or(&module)
+ };
- let module = rel_path
- .components()
- .map(|c| c.as_os_str().to_str().unwrap())
- .collect::<Vec<_>>()
- .join("::");
+ if module == "deprecated_lints" {
+ parse_deprecated_contents(&contents, &mut deprecated_lints);
+ } else {
+ parse_contents(&contents, module, &mut lints);
+ }
+ }
+ (lints, deprecated_lints)
+}
- parse_contents(&content, &module)
+macro_rules! match_tokens {
+ ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
+ {
+ $($(let $capture =)? if let Some((TokenKind::$token $({$($fields)*})?, _x)) = $iter.next() {
+ _x
+ } else {
+ continue;
+ };)*
+ #[allow(clippy::unused_unit)]
+ { ($($($capture,)?)*) }
+ }
+ }
}
-fn parse_contents(content: &str, module: &str) -> impl Iterator<Item = Lint> {
- let lints = DEC_CLIPPY_LINT_RE
- .captures_iter(content)
- .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
- let deprecated = DEC_DEPRECATED_LINT_RE
- .captures_iter(content)
- .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
- // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
- lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
+/// Parse a source file looking for `declare_clippy_lint` macro invocations.
+fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
+ let mut offset = 0usize;
+ let mut iter = tokenize(contents).map(|t| {
+ let range = offset..offset + t.len;
+ offset = range.end;
+ (t.kind, &contents[range])
+ });
+
+ while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_clippy_lint") {
+ let mut iter = iter
+ .by_ref()
+ .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+ // matches `!{`
+ match_tokens!(iter, Bang OpenBrace);
+ match iter.next() {
+ // #[clippy::version = "version"] pub
+ Some((TokenKind::Pound, _)) => {
+ match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
+ },
+ // pub
+ Some((TokenKind::Ident, _)) => (),
+ _ => continue,
+ }
+ let (name, group, desc) = match_tokens!(
+ iter,
+ // LINT_NAME
+ Ident(name) Comma
+ // group,
+ Ident(group) Comma
+ // "description" }
+ Literal{..}(desc) CloseBrace
+ );
+ lints.push(Lint::new(name, group, desc, module));
+ }
}
-/// Collects all .rs files in the `clippy_lints/src` directory
-fn lint_files() -> impl Iterator<Item = walkdir::DirEntry> {
- // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
- // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
- let path = clippy_project_root().join("clippy_lints/src");
- WalkDir::new(path)
- .into_iter()
- .filter_map(Result::ok)
- .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
+fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
+ let mut offset = 0usize;
+ let mut iter = tokenize(contents).map(|t| {
+ let range = offset..offset + t.len;
+ offset = range.end;
+ (t.kind, &contents[range])
+ });
+ while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_deprecated_lint") {
+ let mut iter = iter
+ .by_ref()
+ .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+ let (name, reason) = match_tokens!(
+ iter,
+ // !{
+ Bang OpenBrace
+ // #[clippy::version = "version"]
+ Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket
+ // pub LINT_NAME,
+ Ident Ident(name) Comma
+ // "description"
+ Literal{kind: LiteralKind::Str{..},..}(reason)
+ // }
+ CloseBrace
+ );
+ lints.push(DeprecatedLint::new(name, reason));
+ }
}
-/// Whether a file has had its text changed or not
-#[derive(PartialEq, Debug)]
-struct FileChange {
- changed: bool,
- new_lines: String,
+/// Removes the line splices and surrounding quotes from a string literal
+fn remove_line_splices(s: &str) -> String {
+ let s = s
+ .strip_prefix('r')
+ .unwrap_or(s)
+ .trim_matches('#')
+ .strip_prefix('"')
+ .and_then(|s| s.strip_suffix('"'))
+ .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s));
+ let mut res = String::with_capacity(s.len());
+ unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range]));
+ res
}
/// Replaces a region in a file delimited by two lines matching regexes.
/// # Panics
///
/// Panics if the path could not read or then written
-fn replace_region_in_file<F>(
+fn replace_region_in_file(
+ update_mode: UpdateMode,
path: &Path,
start: &str,
end: &str,
- replace_start: bool,
- write_back: bool,
- replacements: F,
-) -> FileChange
-where
- F: FnOnce() -> Vec<String>,
-{
- let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
- let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
-
- if write_back {
- if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
- panic!("Cannot write to {}: {}", path.display(), e);
- }
- }
- file_change
-}
-
-/// Replaces a region in a text delimited by two lines matching regexes.
-///
-/// * `text` is the input text on which you want to perform the replacement
-/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
-/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
-/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
-/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
-/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
-/// delimiter line is never replaced.
-/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
-///
-/// If you want to perform the replacement on files instead of already parsed text,
-/// use `replace_region_in_file`.
-///
-/// # Example
-///
-/// ```ignore
-/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
-/// let result =
-/// replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
-/// vec!["a different".to_string(), "text".to_string()]
-/// })
-/// .new_lines;
-/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
-/// ```
-///
-/// # Panics
-///
-/// Panics if start or end is not valid regex
-fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
-where
- F: FnOnce() -> Vec<String>,
-{
- let replace_it = replacements();
- let mut in_old_region = false;
- let mut found = false;
- let mut new_lines = vec![];
- let start = Regex::new(start).unwrap();
- let end = Regex::new(end).unwrap();
-
- for line in text.lines() {
- if in_old_region {
- if end.is_match(line) {
- in_old_region = false;
- new_lines.extend(replace_it.clone());
- new_lines.push(line.to_string());
- }
- } else if start.is_match(line) {
- if !replace_start {
- new_lines.push(line.to_string());
+ write_replacement: impl FnMut(&mut String),
+) {
+ let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
+ let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
+ Ok(x) => x,
+ Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()),
+ };
+
+ match update_mode {
+ UpdateMode::Check if contents != new_contents => exit_with_failure(),
+ UpdateMode::Check => (),
+ UpdateMode::Change => {
+ if let Err(e) = fs::write(path, new_contents.as_bytes()) {
+ panic!("Cannot write to `{}`: {}", path.display(), e);
}
- in_old_region = true;
- found = true;
- } else {
- new_lines.push(line.to_string());
- }
- }
-
- if !found {
- // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
- // given text or file. Most likely this is an error on the programmer's side and the Regex
- // is incorrect.
- eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
- std::process::exit(1);
- }
-
- let mut new_lines = new_lines.join("\n");
- if text.ends_with('\n') {
- new_lines.push('\n');
+ },
}
- let changed = new_lines != text;
- FileChange { changed, new_lines }
-}
-
-#[test]
-fn test_parse_contents() {
- let result: Vec<Lint> = parse_contents(
- r#"
-declare_clippy_lint! {
- #[clippy::version = "Hello Clippy!"]
- pub PTR_ARG,
- style,
- "really long \
- text"
}
-declare_clippy_lint!{
- #[clippy::version = "Test version"]
- pub DOC_MARKDOWN,
- pedantic,
- "single line"
-}
-
-/// some doc comment
-declare_deprecated_lint! {
- #[clippy::version = "I'm a version"]
- pub SHOULD_ASSERT_EQ,
- "`assert!()` will be more flexible with RFC 2011"
-}
- "#,
- "module_name",
- )
- .collect();
-
- let expected = vec![
- Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
- Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
- Lint::new(
- "should_assert_eq",
- "Deprecated",
- "`assert!()` will be more flexible with RFC 2011",
- Some("`assert!()` will be more flexible with RFC 2011"),
- "module_name",
- ),
- ];
- assert_eq!(expected, result);
+/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
+/// were found, or the missing delimiter if not.
+fn replace_region_in_text<'a>(
+ text: &str,
+ start: &'a str,
+ end: &'a str,
+ mut write_replacement: impl FnMut(&mut String),
+) -> Result<String, &'a str> {
+ let (text_start, rest) = text.split_once(start).ok_or(start)?;
+ let (_, text_end) = rest.split_once(end).ok_or(end)?;
+
+ let mut res = String::with_capacity(text.len() + 4096);
+ res.push_str(text_start);
+ res.push_str(start);
+ write_replacement(&mut res);
+ res.push_str(end);
+ res.push_str(text_end);
+
+ Ok(res)
}
#[cfg(test)]
use super::*;
#[test]
- fn test_replace_region() {
- let text = "\nabc\n123\n789\ndef\nghi";
- let expected = FileChange {
- changed: true,
- new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
- };
- let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
- vec!["hello world".to_string()]
- });
- assert_eq!(expected, result);
- }
+ fn test_parse_contents() {
+ static CONTENTS: &str = r#"
+ declare_clippy_lint! {
+ #[clippy::version = "Hello Clippy!"]
+ pub PTR_ARG,
+ style,
+ "really long \
+ text"
+ }
- #[test]
- fn test_replace_region_with_start() {
- let text = "\nabc\n123\n789\ndef\nghi";
- let expected = FileChange {
- changed: true,
- new_lines: "\nhello world\ndef\nghi".to_string(),
- };
- let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
- vec!["hello world".to_string()]
- });
+ declare_clippy_lint!{
+ #[clippy::version = "Test version"]
+ pub DOC_MARKDOWN,
+ pedantic,
+ "single line"
+ }
+ "#;
+ let mut result = Vec::new();
+ parse_contents(CONTENTS, "module_name", &mut result);
+
+ let expected = vec![
+ Lint::new("ptr_arg", "style", "\"really long text\"", "module_name"),
+ Lint::new("doc_markdown", "pedantic", "\"single line\"", "module_name"),
+ ];
assert_eq!(expected, result);
}
#[test]
- fn test_replace_region_no_changes() {
- let text = "123\n456\n789";
- let expected = FileChange {
- changed: false,
- new_lines: "123\n456\n789".to_string(),
- };
- let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new);
+ fn test_parse_deprecated_contents() {
+ static DEPRECATED_CONTENTS: &str = r#"
+ /// some doc comment
+ declare_deprecated_lint! {
+ #[clippy::version = "I'm a version"]
+ pub SHOULD_ASSERT_EQ,
+ "`assert!()` will be more flexible with RFC 2011"
+ }
+ "#;
+
+ let mut result = Vec::new();
+ parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
+
+ let expected = vec![DeprecatedLint::new(
+ "should_assert_eq",
+ "\"`assert!()` will be more flexible with RFC 2011\"",
+ )];
assert_eq!(expected, result);
}
#[test]
fn test_usable_lints() {
let lints = vec![
- Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
- Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
+ Lint::new("should_assert_eq2", "Not Deprecated", "\"abc\"", "module_name"),
+ Lint::new("should_assert_eq2", "internal", "\"abc\"", "module_name"),
+ Lint::new("should_assert_eq2", "internal_style", "\"abc\"", "module_name"),
];
let expected = vec![Lint::new(
"should_assert_eq2",
"Not Deprecated",
- "abc",
- None,
+ "\"abc\"",
"module_name",
)];
assert_eq!(expected, Lint::usable_lints(&lints));
#[test]
fn test_by_lint_group() {
let lints = vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
- Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
+ Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
+ Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name"),
+ Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
];
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
expected.insert(
"group1".to_string(),
vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
+ Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
+ Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
],
);
expected.insert(
"group2".to_string(),
- vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
+ vec![Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name")],
);
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
}
- #[test]
- fn test_gen_changelog_lint_list() {
- let lints = vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
- ];
- let expected = vec![
- format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK),
- format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK),
- ];
- assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
- }
-
#[test]
fn test_gen_deprecated() {
let lints = vec![
- Lint::new(
- "should_assert_eq",
- "group1",
- "abc",
- Some("has been superseded by should_assert_eq2"),
- "module_name",
- ),
- Lint::new(
- "another_deprecated",
- "group2",
- "abc",
- Some("will be removed"),
- "module_name",
- ),
+ DeprecatedLint::new("should_assert_eq", "\"has been superseded by should_assert_eq2\""),
+ DeprecatedLint::new("another_deprecated", "\"will be removed\""),
];
let expected = GENERATED_FILE_COMMENT.to_string()
.join("\n")
+ "\n";
- assert_eq!(expected, gen_deprecated(lints.iter()));
- }
-
- #[test]
- #[should_panic]
- fn test_gen_deprecated_fail() {
- let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
- let _deprecated_lints = gen_deprecated(lints.iter());
- }
-
- #[test]
- fn test_gen_modules_list() {
- let lints = vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
- ];
- let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
- assert_eq!(expected, gen_modules_list(lints.iter()));
+ assert_eq!(expected, gen_deprecated(&lints));
}
#[test]
fn test_gen_lint_group_list() {
let lints = vec![
- Lint::new("abc", "group1", "abc", None, "module_name"),
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("internal", "internal_style", "abc", None, "module_name"),
+ Lint::new("abc", "group1", "\"abc\"", "module_name"),
+ Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
+ Lint::new("internal", "internal_style", "\"abc\"", "module_name"),
];
let expected = GENERATED_FILE_COMMENT.to_string()
+ &[