diff options
| author | flip1995 <hello@philkrones.com> | 2020-05-28 15:45:24 +0200 |
|---|---|---|
| committer | flip1995 <hello@philkrones.com> | 2020-05-28 15:45:24 +0200 |
| commit | a0e9f9bd0dca39ccff47baedffa94aca74911a86 (patch) | |
| tree | ff326c800e6850dcb2109284dd5070d1126616f4 /clippy_dev | |
| parent | e820a03d1c905d58b27d7ac6d85f450f9754ba79 (diff) | |
| download | rust-a0e9f9bd0dca39ccff47baedffa94aca74911a86.tar.gz rust-a0e9f9bd0dca39ccff47baedffa94aca74911a86.zip | |
Merge commit '7ea7cd165ad6705603852771bf82cc2fd6560db5' into clippyup2
Diffstat (limited to 'clippy_dev')
| -rw-r--r-- | clippy_dev/src/new_lint.rs | 190 |
1 files changed, 115 insertions, 75 deletions
diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 44b2a5383d2..c0b2dac2f60 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,91 +1,111 @@ use crate::clippy_project_root; -use std::fs::{File, OpenOptions}; -use std::io; +use std::fs::{self, OpenOptions}; use std::io::prelude::*; -use std::io::ErrorKind; -use std::path::Path; +use std::io::{self, ErrorKind}; +use std::path::{Path, PathBuf}; + +struct LintData<'a> { + pass: &'a str, + name: &'a str, + category: &'a str, + project_root: PathBuf, +} + +trait Context { + fn context<C: AsRef<str>>(self, text: C) -> Self; +} + +impl<T> Context for io::Result<T> { + fn context<C: AsRef<str>>(self, text: C) -> Self { + match self { + Ok(t) => Ok(t), + Err(e) => { + let message = format!("{}: {}", text.as_ref(), e); + Err(io::Error::new(ErrorKind::Other, message)) + }, + } + } +} -/// Creates files required to implement and test a new lint and runs `update_lints`. +/// Creates the files required to implement and test a new lint and runs `update_lints`. /// /// # Errors /// -/// This function errors, if the files couldn't be created -pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> { - let pass = pass.expect("`pass` argument is validated by clap"); - let lint_name = lint_name.expect("`name` argument is validated by clap"); - let category = category.expect("`category` argument is validated by clap"); - - match open_files(lint_name) { - Ok((mut test_file, mut lint_file)) => { - let (pass_type, pass_lifetimes, pass_import, context_import) = match 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); - - if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) { - return Err(io::Error::new( - ErrorKind::Other, - format!("Could not write to test file: {}", e), - )); - }; - - if let Err(e) = lint_file.write_all( - get_lint_file_contents( - pass_type, - pass_lifetimes, - lint_name, - &camel_case_name, - category, - pass_import, - context_import, - ) - .as_bytes(), - ) { - return Err(io::Error::new( - ErrorKind::Other, - format!("Could not write to lint file: {}", e), - )); - } - Ok(()) +/// 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<()> { + let lint = LintData { + pass: pass.expect("`pass` argument is validated by clap"), + name: lint_name.expect("`name` argument is validated by clap"), + category: category.expect("`category` argument is validated by clap"), + project_root: clippy_project_root(), + }; + + create_lint(&lint).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`!"); }, - Err(e) => Err(io::Error::new( - ErrorKind::Other, - format!("Unable to create lint: {}", e), - )), - } + }; + + 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, + ); + + let lint_path = format!("clippy_lints/src/{}.rs", lint.name); + write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes()) } -fn open_files(lint_name: &str) -> Result<(File, File), io::Error> { - let project_root = clippy_project_root(); +fn create_test(lint: &LintData) -> io::Result<()> { + fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> { + let mut path = location.into().join(case); + fs::create_dir(&path)?; + write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?; - let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name)); - let lint_file_path = project_root - .join("clippy_lints") - .join("src") - .join(format!("{}.rs", lint_name)); + path.push("src"); + fs::create_dir(&path)?; + let header = format!("// compile-flags: --crate-name={}", lint_name); + write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?; - if Path::new(&test_file_path).exists() { - return Err(io::Error::new( - ErrorKind::AlreadyExists, - format!("test file {:?} already exists", test_file_path), - )); + Ok(()) } - if Path::new(&lint_file_path).exists() { - return Err(io::Error::new( - ErrorKind::AlreadyExists, - format!("lint file {:?} already exists", lint_file_path), - )); + + if lint.category == "cargo" { + let relative_test_dir = format!("tests/ui-cargo/{}", lint.name); + let test_dir = lint.project_root.join(relative_test_dir); + fs::create_dir(&test_dir)?; + + create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?; + create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint") + } else { + let test_path = format!("tests/ui/{}.rs", lint.name); + let test_contents = get_test_file_contents(lint.name, None); + write_file(lint.project_root.join(test_path), test_contents) } +} - let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?; - let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?; +fn write_file<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + fn inner(path: &Path, contents: &[u8]) -> io::Result<()> { + OpenOptions::new() + .write(true) + .create_new(true) + .open(path)? + .write_all(contents) + } - Ok((test_file, lint_file)) + inner(path.as_ref(), contents.as_ref()).context(format!("writing to file: {}", path.as_ref().display())) } fn to_camel_case(name: &str) -> String { @@ -100,8 +120,8 @@ fn to_camel_case(name: &str) -> String { .collect() } -fn get_test_file_contents(lint_name: &str) -> String { - format!( +fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { + let mut contents = format!( "#![warn(clippy::{})] fn main() {{ @@ -109,6 +129,26 @@ fn main() {{ }} ", lint_name + ); + + if let Some(header) = header_commands { + contents = format!("{}\n{}", header, contents); + } + + contents +} + +fn get_manifest_contents(lint_name: &str, hint: &str) -> String { + format!( + r#" +# {} + +[package] +name = "{}" +version = "0.1.0" +publish = false +"#, + hint, lint_name ) } |
