1 use std::fs::{File, OpenOptions};
3 use std::io::prelude::*;
4 use std::io::ErrorKind;
5 use std::path::{Path, PathBuf};
7 pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> {
8 let pass = pass.expect("`pass` argument is validated by clap");
9 let lint_name = lint_name.expect("`name` argument is validated by clap");
10 let category = category.expect("`category` argument is validated by clap");
12 match open_files(lint_name) {
13 Ok((mut test_file, mut lint_file)) => {
14 let (pass_type, pass_lifetimes, pass_import, context_import) = match pass {
15 "early" => ("EarlyLintPass", "", "use syntax::ast::*;", "EarlyContext"),
16 "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"),
18 unreachable!("`pass_type` should only ever be `early` or `late`!");
22 let camel_case_name = to_camel_case(lint_name);
24 if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) {
25 return Err(io::Error::new(
27 format!("Could not write to test file: {}", e),
31 if let Err(e) = lint_file.write_all(
32 get_lint_file_contents(
43 return Err(io::Error::new(
45 format!("Could not write to lint file: {}", e),
50 Err(e) => Err(io::Error::new(
52 format!("Unable to create lint: {}", e),
57 fn open_files(lint_name: &str) -> Result<(File, File), io::Error> {
58 let project_root = project_root()?;
60 let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name));
61 let lint_file_path = project_root
64 .join(format!("{}.rs", lint_name));
66 if Path::new(&test_file_path).exists() {
67 return Err(io::Error::new(
68 ErrorKind::AlreadyExists,
69 format!("test file {:?} already exists", test_file_path),
72 if Path::new(&lint_file_path).exists() {
73 return Err(io::Error::new(
74 ErrorKind::AlreadyExists,
75 format!("lint file {:?} already exists", lint_file_path),
79 let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?;
80 let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?;
82 Ok((test_file, lint_file))
85 fn project_root() -> Result<PathBuf, io::Error> {
86 let current_dir = std::env::current_dir()?;
87 for path in current_dir.ancestors() {
88 let result = std::fs::read_to_string(path.join("Cargo.toml"));
89 if let Err(err) = &result {
90 if err.kind() == io::ErrorKind::NotFound {
95 let content = result?;
96 if content.contains("[package]\nname = \"clippy\"") {
97 return Ok(path.to_path_buf());
100 Err(io::Error::new(ErrorKind::Other, "Unable to find project root"))
103 fn to_camel_case(name: &str) -> String {
109 [&s[0..1].to_uppercase(), &s[1..]].concat()
115 fn get_test_file_contents(lint_name: &str) -> String {
117 "#![warn(clippy::{})]
120 // test code goes here
127 fn get_lint_file_contents(
129 pass_lifetimes: &str,
131 camel_case_name: &str,
134 context_import: &str,
137 "use rustc_lint::{{LintArray, LintPass, {type}, {context_import}}};
138 use rustc_session::{{declare_lint_pass, declare_tool_lint}};
141 declare_clippy_lint! {{
142 /// **What it does:**
144 /// **Why is this bad?**
146 /// **Known problems:** None.
155 \"default lint description\"
158 declare_lint_pass!({name_camel} => [{name_upper}]);
160 impl {type}{lifetimes} for {name_camel} {{}}
163 lifetimes=pass_lifetimes,
164 name_upper=lint_name.to_uppercase(),
165 name_camel=camel_case_name,
167 pass_import=pass_import,
168 context_import=context_import
173 fn test_camel_case() {
175 let s2 = to_camel_case(s);
176 assert_eq!(s2, "ALint");
178 let name = "a_really_long_new_lint";
179 let name2 = to_camel_case(name);
180 assert_eq!(name2, "AReallyLongNewLint");
182 let name3 = "lint__name";
183 let name4 = to_camel_case(name3);
184 assert_eq!(name4, "LintName");