about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-10-11 17:41:00 +0000
committerbors <bors@rust-lang.org>2021-10-11 17:41:00 +0000
commit72723663a385fadba0614a2e79be4955e2fffbb5 (patch)
tree0e4d4ed8968a3fb649b27bfacdf57fb50ad95945
parentbc9ad848eb115164f1b88b07ad58447dbd6069bd (diff)
parent7d9b21b90bc77ab26c8afefc78d6087aff34e4ad (diff)
downloadrust-72723663a385fadba0614a2e79be4955e2fffbb5.tar.gz
rust-72723663a385fadba0614a2e79be4955e2fffbb5.zip
Auto merge of #7793 - mikerite:new_lint-msrv-2, r=Manishearth
Add option to `new_lint` to generate MSRV enabled lint

changelog: none
-rw-r--r--clippy_dev/Cargo.toml1
-rw-r--r--clippy_dev/src/main.rs6
-rw-r--r--clippy_dev/src/new_lint.rs220
3 files changed, 149 insertions, 78 deletions
diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml
index 4a13a452409..affb283017c 100644
--- a/clippy_dev/Cargo.toml
+++ b/clippy_dev/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 [dependencies]
 bytecount = "0.6"
 clap = "2.33"
+indoc = "1.0"
 itertools = "0.10"
 opener = "0.5"
 regex = "1.5"
diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs
index 8fdeba9842a..b5c04efce3b 100644
--- a/clippy_dev/src/main.rs
+++ b/clippy_dev/src/main.rs
@@ -28,6 +28,7 @@ fn main() {
                 matches.value_of("pass"),
                 matches.value_of("name"),
                 matches.value_of("category"),
+                matches.is_present("msrv"),
             ) {
                 Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
                 Err(e) => eprintln!("Unable to create lint: {}", e),
@@ -147,6 +148,11 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                             "internal_warn",
                         ])
                         .takes_value(true),
+                )
+                .arg(
+                    Arg::with_name("msrv")
+                        .long("msrv")
+                        .help("Add MSRV config code to the lint"),
                 ),
         )
         .subcommand(
diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs
index 3a81aaba6de..25320907bb4 100644
--- a/clippy_dev/src/new_lint.rs
+++ b/clippy_dev/src/new_lint.rs
@@ -1,4 +1,5 @@
 use crate::clippy_project_root;
+use indoc::indoc;
 use std::fs::{self, OpenOptions};
 use std::io::prelude::*;
 use std::io::{self, ErrorKind};
@@ -32,7 +33,7 @@ impl<T> Context for io::Result<T> {
 /// # Errors
 ///
 /// This function errors out if the files couldn't be created or written to.
-pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> io::Result<()> {
+pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>, msrv: bool) -> io::Result<()> {
     let lint = LintData {
         pass: pass.expect("`pass` argument is validated by clap"),
         name: lint_name.expect("`name` argument is validated by clap"),
@@ -40,29 +41,12 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str
         project_root: clippy_project_root(),
     };
 
-    create_lint(&lint).context("Unable to create lint implementation")?;
+    create_lint(&lint, msrv).context("Unable to create lint implementation")?;
     create_test(&lint).context("Unable to create a test for the new lint")
 }
 
-fn create_lint(lint: &LintData<'_>) -> io::Result<()> {
-    let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
-        "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
-        "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
-        _ => {
-            unreachable!("`pass_type` should only ever be `early` or `late`!");
-        },
-    };
-
-    let camel_case_name = to_camel_case(lint.name);
-    let lint_contents = get_lint_file_contents(
-        pass_type,
-        pass_lifetimes,
-        lint.name,
-        &camel_case_name,
-        lint.category,
-        pass_import,
-        context_import,
-    );
+fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
+    let lint_contents = get_lint_file_contents(lint, enable_msrv);
 
     let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
     write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())
@@ -122,12 +106,13 @@ fn to_camel_case(name: &str) -> String {
 
 fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
     let mut contents = format!(
-        "#![warn(clippy::{})]
+        indoc! {"
+            #![warn(clippy::{})]
 
-fn main() {{
-    // test code goes here
-}}
-",
+            fn main() {{
+                // test code goes here
+            }}
+        "},
         lint_name
     );
 
@@ -140,64 +125,143 @@ fn main() {{
 
 fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
     format!(
-        r#"
-# {}
+        indoc! {r#"
+            # {}
 
-[package]
-name = "{}"
-version = "0.1.0"
-publish = false
+            [package]
+            name = "{}"
+            version = "0.1.0"
+            publish = false
 
-[workspace]
-"#,
+            [workspace]
+        "#},
         hint, lint_name
     )
 }
 
-fn get_lint_file_contents(
-    pass_type: &str,
-    pass_lifetimes: &str,
-    lint_name: &str,
-    camel_case_name: &str,
-    category: &str,
-    pass_import: &str,
-    context_import: &str,
-) -> String {
-    format!(
-        "use rustc_lint::{{{type}, {context_import}}};
-use rustc_session::{{declare_lint_pass, declare_tool_lint}};
-{pass_import}
-
-declare_clippy_lint! {{
-    /// ### What it does
-    ///
-    /// ### Why is this bad?
-    ///
-    /// ### Example
-    /// ```rust
-    /// // example code where clippy issues a warning
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// // example code which does not raise clippy warning
-    /// ```
-    pub {name_upper},
-    {category},
-    \"default lint description\"
-}}
-
-declare_lint_pass!({name_camel} => [{name_upper}]);
-
-impl {type}{lifetimes} for {name_camel} {{}}
-",
-        type=pass_type,
-        lifetimes=pass_lifetimes,
-        name_upper=lint_name.to_uppercase(),
-        name_camel=camel_case_name,
-        category=category,
-        pass_import=pass_import,
-        context_import=context_import
-    )
+fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
+    let mut result = String::new();
+
+    let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
+        "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
+        "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
+        _ => {
+            unreachable!("`pass_type` should only ever be `early` or `late`!");
+        },
+    };
+
+    let lint_name = lint.name;
+    let pass_name = lint.pass;
+    let category = lint.category;
+    let name_camel = to_camel_case(lint.name);
+    let name_upper = lint_name.to_uppercase();
+
+    result.push_str(&if enable_msrv {
+        format!(
+            indoc! {"
+                use clippy_utils::msrvs;
+                {pass_import}
+                use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
+                use rustc_semver::RustcVersion;
+                use rustc_session::{{declare_tool_lint, impl_lint_pass}};
+
+            "},
+            pass_type = pass_type,
+            pass_import = pass_import,
+            context_import = context_import,
+        )
+    } else {
+        format!(
+            indoc! {"
+                {pass_import}
+                use rustc_lint::{{{context_import}, {pass_type}}};
+                use rustc_session::{{declare_lint_pass, declare_tool_lint}};
+
+            "},
+            pass_import = pass_import,
+            pass_type = pass_type,
+            context_import = context_import
+        )
+    });
+
+    result.push_str(&format!(
+        indoc! {"
+            declare_clippy_lint! {{
+                /// ### What it does
+                ///
+                /// ### Why is this bad?
+                ///
+                /// ### Example
+                /// ```rust
+                /// // example code where clippy issues a warning
+                /// ```
+                /// Use instead:
+                /// ```rust
+                /// // example code which does not raise clippy warning
+                /// ```
+                pub {name_upper},
+                {category},
+                \"default lint description\"
+            }}
+        "},
+        name_upper = name_upper,
+        category = category,
+    ));
+
+    result.push_str(&if enable_msrv {
+        format!(
+            indoc! {"
+                pub struct {name_camel} {{
+                    msrv: Option<RustcVersion>,
+                }}
+
+                impl {name_camel} {{
+                    #[must_use]
+                    pub fn new(msrv: Option<RustcVersion>) -> Self {{
+                        Self {{ msrv }}
+                    }}
+                }}
+
+                impl_lint_pass!({name_camel} => [{name_upper}]);
+
+                impl {pass_type}{pass_lifetimes} for {name_camel} {{
+                    extract_msrv_attr!({context_import});
+                }}
+
+                // TODO: Register the lint pass in `clippy_lints/src/lib.rs`,
+                //       e.g. store.register_{pass_name}_pass(move || Box::new({module_name}::{name_camel}::new(msrv)));
+                // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed.
+                // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`.
+                // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs`
+            "},
+            pass_type = pass_type,
+            pass_lifetimes = pass_lifetimes,
+            pass_name = pass_name,
+            name_upper = name_upper,
+            name_camel = name_camel,
+            module_name = lint_name,
+            context_import = context_import,
+        )
+    } else {
+        format!(
+            indoc! {"
+                declare_lint_pass!({name_camel} => [{name_upper}]);
+
+                impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
+                //
+                // TODO: Register the lint pass in `clippy_lints/src/lib.rs`,
+                //       e.g. store.register_{pass_name}_pass(|| Box::new({module_name}::{name_camel}));
+            "},
+            pass_type = pass_type,
+            pass_lifetimes = pass_lifetimes,
+            pass_name = pass_name,
+            name_upper = name_upper,
+            name_camel = name_camel,
+            module_name = lint_name,
+        )
+    });
+
+    result
 }
 
 #[test]