about summary refs log tree commit diff
path: root/clippy_lints/src/utils/internal_lints
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2023-07-02 14:35:19 +0200
committerPhilipp Krones <hello@philkrones.com>2023-07-02 14:59:02 +0200
commitcb3ecf7b792fdc4b00e61935b9e40ca836752492 (patch)
treea183611f56d9139413f6ab1c78c0619512d3c751 /clippy_lints/src/utils/internal_lints
parentbb33e0343fe37815f6180a861619a9fca6771ce9 (diff)
Merge commit '37f4c1725d3fd7e9c3ffd8783246bc5589debc53' into clippyup
Diffstat (limited to 'clippy_lints/src/utils/internal_lints')
-rw-r--r--clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs87
-rw-r--r--clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs2
-rw-r--r--clippy_lints/src/utils/internal_lints/metadata_collector.rs28
3 files changed, 114 insertions, 3 deletions
diff --git a/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs
new file mode 100644
index 00000000000..570a88a0ed2
--- /dev/null
+++ b/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs
@@ -0,0 +1,87 @@
+use crate::utils::internal_lints::lint_without_lint_pass::is_lint_ref_type;
+use clippy_utils::diagnostics::span_lint_and_help;
+use regex::Regex;
+use rustc_ast as ast;
+use rustc_hir::{Item, ItemKind, Mutability};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks if lint formulations have a standardized format.
+    ///
+    /// ### Why is this bad?
+    /// It's not neccessarily bad, but we try to enforce a standard in Clippy.
+    ///
+    /// ### Example
+    /// `Checks for use...` can be written as `Checks for usage...` .
+    pub ALMOST_STANDARD_LINT_FORMULATION,
+    internal,
+    "lint formulations must have a standardized format."
+}
+
+impl_lint_pass!(AlmostStandardFormulation => [ALMOST_STANDARD_LINT_FORMULATION]);
+
+pub struct AlmostStandardFormulation {
+    standard_formulations: Vec<StandardFormulations<'static>>,
+}
+
+#[derive(Debug)]
+struct StandardFormulations<'a> {
+    wrong_pattern: Regex,
+    correction: &'a str,
+}
+
+impl AlmostStandardFormulation {
+    pub fn new() -> Self {
+        let standard_formulations = vec![StandardFormulations {
+            wrong_pattern: Regex::new("^(Check for|Detects? uses?)").unwrap(),
+            correction: "Checks for",
+        }];
+        Self { standard_formulations }
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+        let mut check_next = false;
+        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
+            let lines = cx
+                .tcx
+                .hir()
+                .attrs(item.hir_id())
+                .iter()
+                .filter_map(|attr| ast::Attribute::doc_str(attr).map(|sym| (sym, attr)));
+            if is_lint_ref_type(cx, ty) {
+                for (line, attr) in lines {
+                    let cur_line = line.as_str().trim();
+                    if check_next && !cur_line.is_empty() {
+                        for formulation in &self.standard_formulations {
+                            let starts_with_correct_formulation = cur_line.starts_with(formulation.correction);
+                            if !starts_with_correct_formulation && formulation.wrong_pattern.is_match(cur_line) {
+                                if let Some(ident) = attr.ident() {
+                                    span_lint_and_help(
+                                        cx,
+                                        ALMOST_STANDARD_LINT_FORMULATION,
+                                        ident.span,
+                                        "non-standard lint formulation",
+                                        None,
+                                        &format!("try using `{}` instead", formulation.correction),
+                                    );
+                                }
+                                return;
+                            }
+                        }
+                        return;
+                    } else if cur_line.contains("What it does") {
+                        check_next = true;
+                    } else if cur_line.contains("Why is this bad") {
+                        // Formulation documentation is done. Can add check to ensure that missing formulation is added
+                        // and add a check if it matches no accepted formulation
+                        return;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
index f8978e30a8e..dced9fcf9ab 100644
--- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
+++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
@@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
 
         for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
             for def_id in def_path_def_ids(cx, module) {
-                for item in cx.tcx.module_children(def_id).iter() {
+                for item in cx.tcx.module_children(def_id) {
                     if_chain! {
                         if let Res::Def(DefKind::Const, item_def_id) = item.res;
                         let ty = cx.tcx.type_of(item_def_id).subst_identity();
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 7a1cd3effae..107a62806a8 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -104,6 +104,8 @@ const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
 /// The version that will be displayed if none has been defined
 const VERSION_DEFAULT_STR: &str = "Unknown";
 
+const CHANGELOG_PATH: &str = "../CHANGELOG.md";
+
 declare_clippy_lint! {
     /// ### What it does
     /// Collects metadata about clippy lints for the website.
@@ -195,8 +197,14 @@ This lint has the following configuration variables:
 
     fn get_markdown_docs(&self) -> String {
         format!(
-            "## Lint Configuration Options\n| <div style=\"width:290px\">Option</div> | Default Value |\n|--|--|\n{}\n\n{}\n",
-            self.configs_to_markdown(ClippyConfiguration::to_markdown_table_entry),
+            r#"# Lint Configuration Options
+
+The following list shows each configuration option, along with a description, its default value, an example
+and lints affected.
+
+---
+
+{}"#,
             self.configs_to_markdown(ClippyConfiguration::to_markdown_paragraph),
         )
     }
@@ -254,6 +262,22 @@ Please use that command to update the file and do not edit it by hand.
             self.get_markdown_docs(),
         )
         .unwrap();
+
+        // Write configuration links to CHANGELOG.md
+        let mut changelog = std::fs::read_to_string(CHANGELOG_PATH).unwrap();
+        let mut changelog_file = OpenOptions::new().read(true).write(true).open(CHANGELOG_PATH).unwrap();
+
+        if let Some(position) = changelog.find("<!-- begin autogenerated links to configuration documentation -->") {
+            // I know this is kinda wasteful, we just don't have regex on `clippy_lints` so... this is the best
+            // we can do AFAIK.
+            changelog = changelog[..position].to_string();
+        }
+        writeln!(
+            changelog_file,
+            "{changelog}<!-- begin autogenerated links to configuration documentation -->\n{}\n<!-- end autogenerated links to configuration documentation -->",
+            self.configs_to_markdown(ClippyConfiguration::to_markdown_link)
+        )
+        .unwrap();
     }
 }