Skip to content

Commit 1a3660f

Browse files
committed
Generate new lints easily
- Add option in clippy_dev to automatically generate boilerplate code for adding new lints
1 parent a0fdca5 commit 1a3660f

File tree

1 file changed

+118
-0
lines changed

1 file changed

+118
-0
lines changed

clippy_dev/src/main.rs

+118
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
use clap::{App, Arg, SubCommand};
44
use clippy_dev::*;
5+
use std::fs::{File, OpenOptions};
6+
use std::io;
7+
use std::io::prelude::*;
8+
use std::io::ErrorKind;
9+
use std::path::PathBuf;
510

611
mod fmt;
712
mod stderr_length_check;
@@ -51,6 +56,26 @@ fn main() {
5156
.help("Checks that util/dev update_lints has been run. Used on CI."),
5257
),
5358
)
59+
.subcommand(
60+
SubCommand::with_name("new_lint")
61+
.about("Create new lint and run util/dev update_lints")
62+
.arg(
63+
Arg::with_name("type")
64+
.long("type")
65+
.help("Create early or late lint")
66+
.takes_value(true)
67+
.possible_values(&["early", "late"])
68+
.required(true),
69+
)
70+
.arg(
71+
Arg::with_name("name")
72+
.short("n")
73+
.long("name")
74+
.help("Name of the new lint in snake case, ex: fn_too_long")
75+
.takes_value(true)
76+
.required(true),
77+
),
78+
)
5479
.arg(
5580
Arg::with_name("limit-stderr-length")
5681
.long("limit-stderr-length")
@@ -75,10 +100,103 @@ fn main() {
75100
update_lints(&UpdateMode::Change);
76101
}
77102
},
103+
("new_lint", Some(matches)) => {
104+
create_new_lint(matches.value_of("type"), matches.value_of("name"));
105+
},
78106
_ => {},
79107
}
80108
}
81109

110+
fn project_root() -> Result<PathBuf, io::Error> {
111+
let current_dir = std::env::current_dir()?;
112+
for path in current_dir.ancestors() {
113+
let result = std::fs::read_to_string(path.join("Cargo.toml"));
114+
if let Err(err) = &result {
115+
if err.kind() == io::ErrorKind::NotFound {
116+
continue;
117+
}
118+
}
119+
120+
let content = result?;
121+
if content.contains("[package]\nname = \"clippy\"") {
122+
return Ok(path.to_path_buf());
123+
}
124+
}
125+
Err(io::Error::new(ErrorKind::Other, "Unable to find project root"))
126+
}
127+
128+
fn open_files(lint_name: &str) -> Result<(File, File), io::Error> {
129+
let project_root = project_root()?;
130+
131+
let test_file_path = project_root.join(format!("tests/ui/{}.rs", lint_name));
132+
let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?;
133+
134+
let lint_file_path = project_root.join(format!("clippy_lints/src/{}.rs", lint_name));
135+
let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?;
136+
137+
Ok((test_file, lint_file))
138+
}
139+
140+
fn create_new_lint(lint_type: Option<&str>, lint_name: Option<&str>) {
141+
// lint_type and lint_name are validated by clap
142+
let lint_type = lint_type.unwrap();
143+
let lint_name = lint_name.unwrap();
144+
145+
match open_files(lint_name) {
146+
Ok((mut test_file, mut lint_file)) => {
147+
let pass_type = match lint_type {
148+
"early" => "EarlyLintPass",
149+
_ => "LateLintPass",
150+
};
151+
152+
let camel_case_name = lint_name
153+
.split('_')
154+
.map(|s| [&s[0..1].to_uppercase(), &s[1..]].concat())
155+
.collect::<std::string::String>();
156+
157+
test_file
158+
.write_all(
159+
format!(
160+
"#![warn(clippy::{})]
161+
162+
fn main() {{
163+
// test code goes here
164+
}}",
165+
lint_name
166+
)
167+
.as_bytes(),
168+
)
169+
.unwrap();
170+
171+
lint_file
172+
.write_all(
173+
format!(
174+
"use rustc::lint::{{LintArray, LintPass, {0}}};
175+
use rustc::declare_lint_pass;
176+
use rustc_session::declare_tool_lint;
177+
178+
declare_clippy_lint! {{
179+
pub {1},
180+
pedantic,
181+
\"Default lint description\"
182+
}}
183+
184+
declare_lint_pass!({2} => [{1}]);
185+
186+
impl {0} for {2} {{}}",
187+
pass_type,
188+
lint_name.to_uppercase(),
189+
camel_case_name
190+
)
191+
.as_bytes(),
192+
)
193+
.unwrap();
194+
update_lints(&UpdateMode::Change);
195+
},
196+
Err(e) => eprintln!("Unable to create lint: {}", e),
197+
}
198+
}
199+
82200
fn print_lints() {
83201
let lint_list = gather_all();
84202
let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list).collect();

0 commit comments

Comments
 (0)