about summary refs log tree commit diff
path: root/clippy_dev
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2022-09-09 13:36:26 +0200
committerPhilipp Krones <hello@philkrones.com>2022-09-09 13:36:26 +0200
commit98bf99e2f8cf8b357d63a67ce67d5fc5ceef8b3c (patch)
tree9737ff22b257f29282e7538d9ecb264451a3c1c0 /clippy_dev
parent854f751b263dfac06dc3f635f8a9f92b8bc51da6 (diff)
downloadrust-98bf99e2f8cf8b357d63a67ce67d5fc5ceef8b3c.tar.gz
rust-98bf99e2f8cf8b357d63a67ce67d5fc5ceef8b3c.zip
Merge commit 'b52fb5234cd7c11ecfae51897a6f7fa52e8777fc' into clippyup
Diffstat (limited to 'clippy_dev')
-rw-r--r--clippy_dev/src/update_lints.rs194
1 files changed, 165 insertions, 29 deletions
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index c503142e5e4..b95061bf81a 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -3,7 +3,7 @@ use aho_corasick::AhoCorasickBuilder;
 use indoc::writedoc;
 use itertools::Itertools;
 use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
-use std::collections::{HashMap, HashSet};
+use std::collections::{BTreeSet, HashMap, HashSet};
 use std::ffi::OsStr;
 use std::fmt::Write;
 use std::fs::{self, OpenOptions};
@@ -124,6 +124,8 @@ fn generate_lint_files(
     let content = gen_lint_group_list("all", all_group_lints);
     process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
 
+    update_docs(update_mode, &usable_lints);
+
     for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
         let content = gen_lint_group_list(&lint_group, lints.iter());
         process_file(
@@ -140,6 +142,62 @@ fn generate_lint_files(
     process_file("tests/ui/rename.rs", update_mode, &content);
 }
 
+fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) {
+    replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| {
+        for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() {
+            writeln!(res, r#"    "{name}","#).unwrap();
+        }
+    });
+
+    if update_mode == UpdateMode::Check {
+        let mut extra = BTreeSet::new();
+        let mut lint_names = usable_lints
+            .iter()
+            .map(|lint| lint.name.clone())
+            .collect::<BTreeSet<_>>();
+        for file in std::fs::read_dir("src/docs").unwrap() {
+            let filename = file.unwrap().file_name().into_string().unwrap();
+            if let Some(name) = filename.strip_suffix(".txt") {
+                if !lint_names.remove(name) {
+                    extra.insert(name.to_string());
+                }
+            }
+        }
+
+        let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names);
+
+        if failed {
+            exit_with_failure();
+        }
+    } else {
+        if std::fs::remove_dir_all("src/docs").is_err() {
+            eprintln!("could not remove src/docs directory");
+        }
+        if std::fs::create_dir("src/docs").is_err() {
+            eprintln!("could not recreate src/docs directory");
+        }
+    }
+    for lint in usable_lints {
+        process_file(
+            Path::new("src/docs").join(lint.name.clone() + ".txt"),
+            update_mode,
+            &lint.documentation,
+        );
+    }
+}
+
+fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
+    if lints.is_empty() {
+        return false;
+    }
+    println!("{}", header);
+    for lint in lints.iter().sorted() {
+        println!("    {}", lint);
+    }
+    println!();
+    true
+}
+
 pub fn print_lints() {
     let (lint_list, _, _) = gather_all();
     let usable_lints = Lint::usable_lints(&lint_list);
@@ -589,17 +647,26 @@ struct Lint {
     desc: String,
     module: String,
     declaration_range: Range<usize>,
+    documentation: String,
 }
 
 impl Lint {
     #[must_use]
-    fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
+    fn new(
+        name: &str,
+        group: &str,
+        desc: &str,
+        module: &str,
+        declaration_range: Range<usize>,
+        documentation: String,
+    ) -> Self {
         Self {
             name: name.to_lowercase(),
             group: group.into(),
             desc: remove_line_splices(desc),
             module: module.into(),
             declaration_range,
+            documentation,
         }
     }
 
@@ -852,27 +919,35 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
          }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
     ) {
         let start = range.start;
-
-        let mut iter = iter
-            .by_ref()
-            .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+        let mut docs = String::with_capacity(128);
+        let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace));
         // matches `!{`
         match_tokens!(iter, Bang OpenBrace);
-        match iter.next() {
-            // #[clippy::version = "version"] pub
-            Some(LintDeclSearchResult {
-                token_kind: TokenKind::Pound,
-                ..
-            }) => {
-                match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
-            },
-            // pub
-            Some(LintDeclSearchResult {
-                token_kind: TokenKind::Ident,
-                ..
-            }) => (),
-            _ => continue,
+        let mut in_code = false;
+        while let Some(t) = iter.next() {
+            match t.token_kind {
+                TokenKind::LineComment { .. } => {
+                    if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) {
+                        if line.starts_with("```") {
+                            docs += "```\n";
+                            in_code = !in_code;
+                        } else if !(in_code && line.starts_with("# ")) {
+                            docs += line;
+                            docs.push('\n');
+                        }
+                    }
+                },
+                TokenKind::Pound => {
+                    match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
+                    break;
+                },
+                TokenKind::Ident => {
+                    break;
+                },
+                _ => {},
+            }
         }
+        docs.pop(); // remove final newline
 
         let (name, group, desc) = match_tokens!(
             iter,
@@ -890,7 +965,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
             ..
         }) = iter.next()
         {
-            lints.push(Lint::new(name, group, desc, module, start..range.end));
+            lints.push(Lint::new(name, group, desc, module, start..range.end, docs));
         }
     }
 }
@@ -977,7 +1052,11 @@ fn remove_line_splices(s: &str) -> String {
         .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]));
+    unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| {
+        if ch.is_ok() {
+            res.push_str(&s[range]);
+        }
+    });
     res
 }
 
@@ -1116,6 +1195,7 @@ mod tests {
                 "\"really long text\"",
                 "module_name",
                 Range::default(),
+                String::new(),
             ),
             Lint::new(
                 "doc_markdown",
@@ -1123,6 +1203,7 @@ mod tests {
                 "\"single line\"",
                 "module_name",
                 Range::default(),
+                String::new(),
             ),
         ];
         assert_eq!(expected, result);
@@ -1162,6 +1243,7 @@ mod tests {
                 "\"abc\"",
                 "module_name",
                 Range::default(),
+                String::new(),
             ),
             Lint::new(
                 "should_assert_eq2",
@@ -1169,6 +1251,7 @@ mod tests {
                 "\"abc\"",
                 "module_name",
                 Range::default(),
+                String::new(),
             ),
             Lint::new(
                 "should_assert_eq2",
@@ -1176,6 +1259,7 @@ mod tests {
                 "\"abc\"",
                 "module_name",
                 Range::default(),
+                String::new(),
             ),
         ];
         let expected = vec![Lint::new(
@@ -1184,6 +1268,7 @@ mod tests {
             "\"abc\"",
             "module_name",
             Range::default(),
+            String::new(),
         )];
         assert_eq!(expected, Lint::usable_lints(&lints));
     }
@@ -1191,22 +1276,51 @@ mod tests {
     #[test]
     fn test_by_lint_group() {
         let lints = vec![
-            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
+            Lint::new(
+                "should_assert_eq",
+                "group1",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+                String::new(),
+            ),
             Lint::new(
                 "should_assert_eq2",
                 "group2",
                 "\"abc\"",
                 "module_name",
                 Range::default(),
+                String::new(),
+            ),
+            Lint::new(
+                "incorrect_match",
+                "group1",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+                String::new(),
             ),
-            Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
         ];
         let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
         expected.insert(
             "group1".to_string(),
             vec![
-                Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
-                Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
+                Lint::new(
+                    "should_assert_eq",
+                    "group1",
+                    "\"abc\"",
+                    "module_name",
+                    Range::default(),
+                    String::new(),
+                ),
+                Lint::new(
+                    "incorrect_match",
+                    "group1",
+                    "\"abc\"",
+                    "module_name",
+                    Range::default(),
+                    String::new(),
+                ),
             ],
         );
         expected.insert(
@@ -1217,6 +1331,7 @@ mod tests {
                 "\"abc\"",
                 "module_name",
                 Range::default(),
+                String::new(),
             )],
         );
         assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
@@ -1255,9 +1370,30 @@ mod tests {
     #[test]
     fn test_gen_lint_group_list() {
         let lints = vec![
-            Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
-            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
-            Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
+            Lint::new(
+                "abc",
+                "group1",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+                String::new(),
+            ),
+            Lint::new(
+                "should_assert_eq",
+                "group1",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+                String::new(),
+            ),
+            Lint::new(
+                "internal",
+                "internal_style",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+                String::new(),
+            ),
         ];
         let expected = GENERATED_FILE_COMMENT.to_string()
             + &[