aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-11-20 13:27:28 +0000
committerAkshay <[email protected]>2021-11-20 13:27:28 +0000
commitf27372061a0effe3b00d400f4e577b9d9e0ad4c0 (patch)
treeedb9a9644a7ce6e8c28e937cdc5e4d62d45b4cb3
parenta5c3e679b06536bb43085b1a978854a73274af10 (diff)
add config option to fix and check
-rw-r--r--Cargo.lock10
-rw-r--r--bin/Cargo.toml10
-rw-r--r--bin/src/config.rs148
-rw-r--r--bin/src/err.rs2
-rw-r--r--bin/src/explain.rs7
-rw-r--r--bin/src/fix.rs12
-rw-r--r--bin/src/fix/all.rs18
-rw-r--r--bin/src/fix/single.rs8
-rw-r--r--bin/src/lib.rs9
-rw-r--r--bin/src/lint.rs17
-rw-r--r--bin/src/utils.rs24
-rw-r--r--lib/src/lib.rs19
12 files changed, 198 insertions, 86 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 08881fd..2da3c9d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -520,6 +520,7 @@ dependencies = [
520 "similar 2.1.0", 520 "similar 2.1.0",
521 "strip-ansi-escapes", 521 "strip-ansi-escapes",
522 "thiserror", 522 "thiserror",
523 "toml",
523 "vfs", 524 "vfs",
524] 525]
525 526
@@ -613,6 +614,15 @@ dependencies = [
613] 614]
614 615
615[[package]] 616[[package]]
617name = "toml"
618version = "0.5.8"
619source = "registry+https://github.com/rust-lang/crates.io-index"
620checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
621dependencies = [
622 "serde",
623]
624
625[[package]]
616name = "unicase" 626name = "unicase"
617version = "2.6.0" 627version = "2.6.0"
618source = "registry+https://github.com/rust-lang/crates.io-index" 628source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/bin/Cargo.toml b/bin/Cargo.toml
index 7c48083..c898792 100644
--- a/bin/Cargo.toml
+++ b/bin/Cargo.toml
@@ -23,14 +23,14 @@ thiserror = "1.0.30"
23similar = "2.1.0" 23similar = "2.1.0"
24vfs = { path = "../vfs" } 24vfs = { path = "../vfs" }
25lib = { path = "../lib" } 25lib = { path = "../lib" }
26 26toml = "0.5.8"
27[dependencies.serde_json]
28version = "1.0.68"
29optional = true
30 27
31[dependencies.serde] 28[dependencies.serde]
32version = "1.0.68" 29version = "1.0.68"
33features = [ "derive" ] 30features = [ "derive" ]
31
32[dependencies.serde_json]
33version = "1.0.68"
34optional = true 34optional = true
35 35
36[dev-dependencies] 36[dev-dependencies]
@@ -38,4 +38,4 @@ insta = "1.8.0"
38strip-ansi-escapes = "0.1.1" 38strip-ansi-escapes = "0.1.1"
39 39
40[features] 40[features]
41json = [ "lib/json-out", "serde_json", "serde" ] 41json = [ "lib/json-out", "serde_json" ]
diff --git a/bin/src/config.rs b/bin/src/config.rs
index d3944ac..98b41da 100644
--- a/bin/src/config.rs
+++ b/bin/src/config.rs
@@ -1,8 +1,15 @@
1use std::{default::Default, fmt, fs, path::PathBuf, str::FromStr}; 1use std::{
2 default::Default,
3 fmt, fs,
4 path::{Path, PathBuf},
5 str::FromStr,
6};
2 7
3use crate::{dirs, err::ConfigErr}; 8use crate::{dirs, err::ConfigErr, utils, LintMap};
4 9
5use clap::Parser; 10use clap::Parser;
11use lib::LINTS;
12use serde::{Deserialize, Serialize};
6use vfs::ReadOnlyVfs; 13use vfs::ReadOnlyVfs;
7 14
8#[derive(Parser, Debug)] 15#[derive(Parser, Debug)]
@@ -43,6 +50,10 @@ pub struct Check {
43 #[clap(short = 'o', long, default_value_t, parse(try_from_str))] 50 #[clap(short = 'o', long, default_value_t, parse(try_from_str))]
44 pub format: OutFormat, 51 pub format: OutFormat,
45 52
53 /// Path to statix.toml
54 #[clap(short = 'c', long = "config")]
55 pub conf_path: Option<PathBuf>,
56
46 /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout 57 /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout
47 #[clap(short, long = "stdin")] 58 #[clap(short, long = "stdin")]
48 pub streaming: bool, 59 pub streaming: bool,
@@ -65,6 +76,10 @@ impl Check {
65 vfs(files.collect::<Vec<_>>()) 76 vfs(files.collect::<Vec<_>>())
66 } 77 }
67 } 78 }
79
80 pub fn lints(&self) -> Result<LintMap, ConfigErr> {
81 lints(self.conf_path.as_ref())
82 }
68} 83}
69 84
70#[derive(Parser, Debug)] 85#[derive(Parser, Debug)]
@@ -85,6 +100,10 @@ pub struct Fix {
85 #[clap(short, long = "dry-run")] 100 #[clap(short, long = "dry-run")]
86 pub diff_only: bool, 101 pub diff_only: bool,
87 102
103 /// Path to statix.toml
104 #[clap(short = 'c', long = "config")]
105 pub conf_path: Option<PathBuf>,
106
88 /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout 107 /// Enable "streaming" mode, accept file on stdin, output diagnostics on stdout
89 #[clap(short, long = "stdin")] 108 #[clap(short, long = "stdin")]
90 pub streaming: bool, 109 pub streaming: bool,
@@ -125,6 +144,10 @@ impl Fix {
125 FixOut::Write 144 FixOut::Write
126 } 145 }
127 } 146 }
147
148 pub fn lints(&self) -> Result<LintMap, ConfigErr> {
149 lints(self.conf_path.as_ref())
150 }
128} 151}
129 152
130#[derive(Parser, Debug)] 153#[derive(Parser, Debug)]
@@ -181,6 +204,72 @@ pub struct Explain {
181 pub target: u32, 204 pub target: u32,
182} 205}
183 206
207#[derive(Debug, Copy, Clone)]
208pub enum OutFormat {
209 #[cfg(feature = "json")]
210 Json,
211 Errfmt,
212 StdErr,
213}
214
215impl Default for OutFormat {
216 fn default() -> Self {
217 OutFormat::StdErr
218 }
219}
220
221impl fmt::Display for OutFormat {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 write!(
224 f,
225 "{}",
226 match self {
227 #[cfg(feature = "json")]
228 Self::Json => "json",
229 Self::Errfmt => "errfmt",
230 Self::StdErr => "stderr",
231 }
232 )
233 }
234}
235
236impl FromStr for OutFormat {
237 type Err = &'static str;
238
239 fn from_str(value: &str) -> Result<Self, Self::Err> {
240 match value.to_ascii_lowercase().as_str() {
241 #[cfg(feature = "json")]
242 "json" => Ok(Self::Json),
243 #[cfg(not(feature = "json"))]
244 "json" => Err("statix was not compiled with the `json` feature flag"),
245 "errfmt" => Ok(Self::Errfmt),
246 "stderr" => Ok(Self::StdErr),
247 _ => Err("unknown output format, try: json, errfmt"),
248 }
249 }
250}
251
252#[derive(Serialize, Deserialize, Debug)]
253pub struct ConfFile {
254 checks: Vec<String>,
255}
256
257impl ConfFile {
258 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, ConfigErr> {
259 let path = path.as_ref();
260 let config_file = fs::read_to_string(path).map_err(ConfigErr::InvalidPath)?;
261 toml::de::from_str(&config_file).map_err(|err| {
262 let pos = err.line_col();
263 let msg = if let Some((line, col)) = pos {
264 format!("line {}, col {}", line, col)
265 } else {
266 "unknown".to_string()
267 };
268 ConfigErr::ConfFileParse(msg)
269 })
270 }
271}
272
184fn parse_line_col(src: &str) -> Result<(usize, usize), ConfigErr> { 273fn parse_line_col(src: &str) -> Result<(usize, usize), ConfigErr> {
185 let parts = src.split(','); 274 let parts = src.split(',');
186 match parts.collect::<Vec<_>>().as_slice() { 275 match parts.collect::<Vec<_>>().as_slice() {
@@ -225,47 +314,18 @@ fn vfs(files: Vec<PathBuf>) -> Result<ReadOnlyVfs, ConfigErr> {
225 Ok(vfs) 314 Ok(vfs)
226} 315}
227 316
228#[derive(Debug, Copy, Clone)] 317fn lints(conf_path: Option<&PathBuf>) -> Result<LintMap, ConfigErr> {
229pub enum OutFormat { 318 if let Some(conf_path) = conf_path {
230 #[cfg(feature = "json")] 319 let config_file = ConfFile::from_path(conf_path)?;
231 Json, 320 Ok(utils::lint_map_of(
232 Errfmt, 321 (&*LINTS)
233 StdErr, 322 .into_iter()
234} 323 .filter(|l| config_file.checks.iter().any(|check| check == l.name()))
235 324 .cloned()
236impl Default for OutFormat { 325 .collect::<Vec<_>>()
237 fn default() -> Self { 326 .as_slice(),
238 OutFormat::StdErr 327 ))
239 } 328 } else {
240} 329 Ok(utils::lint_map())
241
242impl fmt::Display for OutFormat {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 write!(
245 f,
246 "{}",
247 match self {
248 #[cfg(feature = "json")]
249 Self::Json => "json",
250 Self::Errfmt => "errfmt",
251 Self::StdErr => "stderr",
252 }
253 )
254 }
255}
256
257impl FromStr for OutFormat {
258 type Err = &'static str;
259
260 fn from_str(value: &str) -> Result<Self, Self::Err> {
261 match value.to_ascii_lowercase().as_str() {
262 #[cfg(feature = "json")]
263 "json" => Ok(Self::Json),
264 #[cfg(not(feature = "json"))]
265 "json" => Err("statix was not compiled with the `json` feature flag"),
266 "errfmt" => Ok(Self::Errfmt),
267 "stderr" => Ok(Self::StdErr),
268 _ => Err("unknown output format, try: json, errfmt"),
269 }
270 } 330 }
271} 331}
diff --git a/bin/src/err.rs b/bin/src/err.rs
index b776d9f..f44d27b 100644
--- a/bin/src/err.rs
+++ b/bin/src/err.rs
@@ -14,6 +14,8 @@ pub enum ConfigErr {
14 InvalidPosition(String), 14 InvalidPosition(String),
15 #[error("unable to parse `{0}` as warning code")] 15 #[error("unable to parse `{0}` as warning code")]
16 InvalidWarningCode(String), 16 InvalidWarningCode(String),
17 #[error("unable to parse config file, error at: `{0}`")]
18 ConfFileParse(String),
17} 19}
18 20
19// #[derive(Error, Debug)] 21// #[derive(Error, Debug)]
diff --git a/bin/src/explain.rs b/bin/src/explain.rs
index de171aa..bcb8eed 100644
--- a/bin/src/explain.rs
+++ b/bin/src/explain.rs
@@ -1,11 +1,10 @@
1use crate::err::ExplainErr; 1use crate::{err::ExplainErr, utils};
2
3use lib::LINTS;
4 2
5pub fn explain(code: u32) -> Result<&'static str, ExplainErr> { 3pub fn explain(code: u32) -> Result<&'static str, ExplainErr> {
4 let lints = utils::lint_map();
6 match code { 5 match code {
7 0 => Ok("syntax error"), 6 0 => Ok("syntax error"),
8 _ => LINTS 7 _ => lints
9 .values() 8 .values()
10 .flatten() 9 .flatten()
11 .find(|l| l.code() == code) 10 .find(|l| l.code() == code)
diff --git a/bin/src/fix.rs b/bin/src/fix.rs
index e4ea94d..4268567 100644
--- a/bin/src/fix.rs
+++ b/bin/src/fix.rs
@@ -1,19 +1,21 @@
1use std::borrow::Cow; 1use std::borrow::Cow;
2 2
3use crate::LintMap;
4
3use rnix::TextRange; 5use rnix::TextRange;
4 6
5mod all; 7mod all;
6use all::all; 8use all::all_with;
7 9
8mod single; 10mod single;
9use single::single; 11use single::single;
10 12
11type Source<'a> = Cow<'a, str>; 13type Source<'a> = Cow<'a, str>;
12 14
13#[derive(Debug)]
14pub struct FixResult<'a> { 15pub struct FixResult<'a> {
15 pub src: Source<'a>, 16 pub src: Source<'a>,
16 pub fixed: Vec<Fixed>, 17 pub fixed: Vec<Fixed>,
18 pub lints: &'a LintMap,
17} 19}
18 20
19#[derive(Debug, Clone)] 21#[derive(Debug, Clone)]
@@ -23,10 +25,11 @@ pub struct Fixed {
23} 25}
24 26
25impl<'a> FixResult<'a> { 27impl<'a> FixResult<'a> {
26 fn empty(src: Source<'a>) -> Self { 28 fn empty(src: Source<'a>, lints: &'a LintMap) -> Self {
27 Self { 29 Self {
28 src, 30 src,
29 fixed: Vec::new(), 31 fixed: Vec::new(),
32 lints,
30 } 33 }
31 } 34 }
32} 35}
@@ -43,8 +46,9 @@ pub mod main {
43 46
44 pub fn all(fix_config: FixConfig) -> Result<(), StatixErr> { 47 pub fn all(fix_config: FixConfig) -> Result<(), StatixErr> {
45 let vfs = fix_config.vfs()?; 48 let vfs = fix_config.vfs()?;
49 let lints = fix_config.lints()?;
46 for entry in vfs.iter() { 50 for entry in vfs.iter() {
47 match (fix_config.out(), super::all(entry.contents)) { 51 match (fix_config.out(), super::all_with(entry.contents, &lints)) {
48 (FixOut::Diff, fix_result) => { 52 (FixOut::Diff, fix_result) => {
49 let src = fix_result 53 let src = fix_result
50 .map(|r| r.src) 54 .map(|r| r.src)
diff --git a/bin/src/fix/all.rs b/bin/src/fix/all.rs
index 7f04f2c..bbc39e8 100644
--- a/bin/src/fix/all.rs
+++ b/bin/src/fix/all.rs
@@ -1,18 +1,21 @@
1use std::borrow::Cow; 1use std::borrow::Cow;
2 2
3use lib::{Report, LINTS}; 3use lib::Report;
4use rnix::{parser::ParseError as RnixParseErr, WalkEvent}; 4use rnix::{parser::ParseError as RnixParseErr, WalkEvent};
5 5
6use crate::fix::{FixResult, Fixed}; 6use crate::{
7 fix::{FixResult, Fixed},
8 LintMap,
9};
7 10
8fn collect_fixes(source: &str) -> Result<Vec<Report>, RnixParseErr> { 11fn collect_fixes(source: &str, lints: &LintMap) -> Result<Vec<Report>, RnixParseErr> {
9 let parsed = rnix::parse(source).as_result()?; 12 let parsed = rnix::parse(source).as_result()?;
10 13
11 Ok(parsed 14 Ok(parsed
12 .node() 15 .node()
13 .preorder_with_tokens() 16 .preorder_with_tokens()
14 .filter_map(|event| match event { 17 .filter_map(|event| match event {
15 WalkEvent::Enter(child) => LINTS.get(&child.kind()).map(|rules| { 18 WalkEvent::Enter(child) => lints.get(&child.kind()).map(|rules| {
16 rules 19 rules
17 .iter() 20 .iter()
18 .filter_map(|rule| rule.validate(&child)) 21 .filter_map(|rule| rule.validate(&child))
@@ -54,7 +57,7 @@ fn reorder(mut reports: Vec<Report>) -> Vec<Report> {
54impl<'a> Iterator for FixResult<'a> { 57impl<'a> Iterator for FixResult<'a> {
55 type Item = FixResult<'a>; 58 type Item = FixResult<'a>;
56 fn next(&mut self) -> Option<Self::Item> { 59 fn next(&mut self) -> Option<Self::Item> {
57 let all_reports = collect_fixes(&self.src).ok()?; 60 let all_reports = collect_fixes(&self.src, &self.lints).ok()?;
58 if all_reports.is_empty() { 61 if all_reports.is_empty() {
59 return None; 62 return None;
60 } 63 }
@@ -74,13 +77,14 @@ impl<'a> Iterator for FixResult<'a> {
74 Some(FixResult { 77 Some(FixResult {
75 src: self.src.clone(), 78 src: self.src.clone(),
76 fixed, 79 fixed,
80 lints: self.lints,
77 }) 81 })
78 } 82 }
79} 83}
80 84
81pub fn all(src: &str) -> Option<FixResult> { 85pub fn all_with<'a>(src: &'a str, lints: &'a LintMap) -> Option<FixResult<'a>> {
82 let src = Cow::from(src); 86 let src = Cow::from(src);
83 let _ = rnix::parse(&src).as_result().ok()?; 87 let _ = rnix::parse(&src).as_result().ok()?;
84 let initial = FixResult::empty(src); 88 let initial = FixResult::empty(src, lints);
85 initial.into_iter().last() 89 initial.into_iter().last()
86} 90}
diff --git a/bin/src/fix/single.rs b/bin/src/fix/single.rs
index b91dc45..d95cfda 100644
--- a/bin/src/fix/single.rs
+++ b/bin/src/fix/single.rs
@@ -1,10 +1,9 @@
1use std::{borrow::Cow, convert::TryFrom}; 1use std::{borrow::Cow, convert::TryFrom};
2 2
3use lib::{Report, LINTS}; 3use lib::Report;
4use rnix::{TextSize, WalkEvent}; 4use rnix::{TextSize, WalkEvent};
5 5
6use crate::err::SingleFixErr; 6use crate::{err::SingleFixErr, fix::Source, utils};
7use crate::fix::Source;
8 7
9pub struct SingleFixResult<'δ> { 8pub struct SingleFixResult<'δ> {
10 pub src: Source<'δ>, 9 pub src: Source<'δ>,
@@ -31,12 +30,13 @@ fn pos_to_byte(line: usize, col: usize, src: &str) -> Result<TextSize, SingleFix
31fn find(offset: TextSize, src: &str) -> Result<Report, SingleFixErr> { 30fn find(offset: TextSize, src: &str) -> Result<Report, SingleFixErr> {
32 // we don't really need the source to form a completely parsed tree 31 // we don't really need the source to form a completely parsed tree
33 let parsed = rnix::parse(src); 32 let parsed = rnix::parse(src);
33 let lints = utils::lint_map();
34 34
35 parsed 35 parsed
36 .node() 36 .node()
37 .preorder_with_tokens() 37 .preorder_with_tokens()
38 .filter_map(|event| match event { 38 .filter_map(|event| match event {
39 WalkEvent::Enter(child) => LINTS.get(&child.kind()).map(|rules| { 39 WalkEvent::Enter(child) => lints.get(&child.kind()).map(|rules| {
40 rules 40 rules
41 .iter() 41 .iter()
42 .filter_map(|rule| rule.validate(&child)) 42 .filter_map(|rule| rule.validate(&child))
diff --git a/bin/src/lib.rs b/bin/src/lib.rs
index 49c1a41..0105334 100644
--- a/bin/src/lib.rs
+++ b/bin/src/lib.rs
@@ -5,3 +5,12 @@ pub mod explain;
5pub mod fix; 5pub mod fix;
6pub mod lint; 6pub mod lint;
7pub mod traits; 7pub mod traits;
8
9mod utils;
10
11use std::collections::HashMap;
12
13use lib::Lint;
14use rnix::SyntaxKind;
15
16pub type LintMap = HashMap<SyntaxKind, Vec<&'static Box<dyn Lint>>>;
diff --git a/bin/src/lint.rs b/bin/src/lint.rs
index 1138c23..3482d46 100644
--- a/bin/src/lint.rs
+++ b/bin/src/lint.rs
@@ -1,4 +1,6 @@
1use lib::{Report, LINTS}; 1use crate::{utils, LintMap};
2
3use lib::Report;
2use rnix::WalkEvent; 4use rnix::WalkEvent;
3use vfs::{FileId, VfsEntry}; 5use vfs::{FileId, VfsEntry};
4 6
@@ -8,18 +10,17 @@ pub struct LintResult {
8 pub reports: Vec<Report>, 10 pub reports: Vec<Report>,
9} 11}
10 12
11pub fn lint(vfs_entry: VfsEntry) -> LintResult { 13pub fn lint_with(vfs_entry: VfsEntry, lints: &LintMap) -> LintResult {
12 let file_id = vfs_entry.file_id; 14 let file_id = vfs_entry.file_id;
13 let source = vfs_entry.contents; 15 let source = vfs_entry.contents;
14 let parsed = rnix::parse(source); 16 let parsed = rnix::parse(source);
15 17
16 let error_reports = parsed.errors().into_iter().map(Report::from_parse_err); 18 let error_reports = parsed.errors().into_iter().map(Report::from_parse_err);
17
18 let reports = parsed 19 let reports = parsed
19 .node() 20 .node()
20 .preorder_with_tokens() 21 .preorder_with_tokens()
21 .filter_map(|event| match event { 22 .filter_map(|event| match event {
22 WalkEvent::Enter(child) => LINTS.get(&child.kind()).map(|rules| { 23 WalkEvent::Enter(child) => lints.get(&child.kind()).map(|rules| {
23 rules 24 rules
24 .iter() 25 .iter()
25 .filter_map(|rule| rule.validate(&child)) 26 .filter_map(|rule| rule.validate(&child))
@@ -34,15 +35,21 @@ pub fn lint(vfs_entry: VfsEntry) -> LintResult {
34 LintResult { file_id, reports } 35 LintResult { file_id, reports }
35} 36}
36 37
38pub fn lint(vfs_entry: VfsEntry) -> LintResult {
39 lint_with(vfs_entry, &utils::lint_map())
40}
41
37pub mod main { 42pub mod main {
38 use std::io; 43 use std::io;
39 44
40 use super::lint; 45 use super::lint_with;
41 use crate::{config::Check as CheckConfig, err::StatixErr, traits::WriteDiagnostic}; 46 use crate::{config::Check as CheckConfig, err::StatixErr, traits::WriteDiagnostic};
42 47
43 pub fn main(check_config: CheckConfig) -> Result<(), StatixErr> { 48 pub fn main(check_config: CheckConfig) -> Result<(), StatixErr> {
44 let vfs = check_config.vfs()?; 49 let vfs = check_config.vfs()?;
45 let mut stdout = io::stdout(); 50 let mut stdout = io::stdout();
51 let lints = check_config.lints()?;
52 let lint = |vfs_entry| lint_with(vfs_entry, &lints);
46 vfs.iter().map(lint).for_each(|r| { 53 vfs.iter().map(lint).for_each(|r| {
47 stdout.write(&r, &vfs, check_config.format).unwrap(); 54 stdout.write(&r, &vfs, check_config.format).unwrap();
48 }); 55 });
diff --git a/bin/src/utils.rs b/bin/src/utils.rs
new file mode 100644
index 0000000..747a761
--- /dev/null
+++ b/bin/src/utils.rs
@@ -0,0 +1,24 @@
1use std::collections::HashMap;
2
3use lib::{Lint, LINTS};
4use rnix::SyntaxKind;
5
6pub fn lint_map_of(
7 lints: &[&'static Box<dyn Lint>],
8) -> HashMap<SyntaxKind, Vec<&'static Box<dyn Lint>>> {
9 let mut map = HashMap::new();
10 for lint in lints.iter() {
11 let lint = *lint;
12 let matches = lint.match_kind();
13 for m in matches {
14 map.entry(m)
15 .and_modify(|v: &mut Vec<_>| v.push(lint))
16 .or_insert_with(|| vec![lint]);
17 }
18 }
19 map
20}
21
22pub fn lint_map() -> HashMap<SyntaxKind, Vec<&'static Box<dyn Lint>>> {
23 lint_map_of(&*LINTS)
24}
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index 5347666..d96d9eb 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -255,31 +255,24 @@ pub trait Lint: Metadata + Explain + Rule + Send + Sync {}
255/// 255///
256/// See `lints.rs` for usage. 256/// See `lints.rs` for usage.
257#[macro_export] 257#[macro_export]
258macro_rules! lint_map { 258macro_rules! lints {
259 ($($s:ident),*,) => { 259 ($($s:ident),*,) => {
260 lint_map!($($s),*); 260 lints!($($s),*);
261 }; 261 };
262 ($($s:ident),*) => { 262 ($($s:ident),*) => {
263 use ::std::collections::HashMap;
264 use ::rnix::SyntaxKind;
265 $( 263 $(
266 mod $s; 264 mod $s;
267 )* 265 )*
268 ::lazy_static::lazy_static! { 266 ::lazy_static::lazy_static! {
269 pub static ref LINTS: HashMap<SyntaxKind, Vec<&'static Box<dyn $crate::Lint>>> = { 267 pub static ref LINTS: Vec<&'static Box<dyn $crate::Lint>> = {
270 let mut map = HashMap::new(); 268 let mut v = Vec::new();
271 $( 269 $(
272 { 270 {
273 let temp_lint = &*$s::LINT; 271 let temp_lint = &*$s::LINT;
274 let temp_matches = temp_lint.match_kind(); 272 v.push(temp_lint);
275 for temp_match in temp_matches {
276 map.entry(temp_match)
277 .and_modify(|v: &mut Vec<_>| v.push(temp_lint))
278 .or_insert_with(|| vec![temp_lint]);
279 }
280 } 273 }
281 )* 274 )*
282 map 275 v
283 }; 276 };
284 } 277 }
285 } 278 }