diff options
author | Akshay <[email protected]> | 2021-11-20 13:27:28 +0000 |
---|---|---|
committer | Akshay <[email protected]> | 2021-11-20 13:27:28 +0000 |
commit | f27372061a0effe3b00d400f4e577b9d9e0ad4c0 (patch) | |
tree | edb9a9644a7ce6e8c28e937cdc5e4d62d45b4cb3 | |
parent | a5c3e679b06536bb43085b1a978854a73274af10 (diff) |
add config option to fix and check
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | bin/Cargo.toml | 10 | ||||
-rw-r--r-- | bin/src/config.rs | 148 | ||||
-rw-r--r-- | bin/src/err.rs | 2 | ||||
-rw-r--r-- | bin/src/explain.rs | 7 | ||||
-rw-r--r-- | bin/src/fix.rs | 12 | ||||
-rw-r--r-- | bin/src/fix/all.rs | 18 | ||||
-rw-r--r-- | bin/src/fix/single.rs | 8 | ||||
-rw-r--r-- | bin/src/lib.rs | 9 | ||||
-rw-r--r-- | bin/src/lint.rs | 17 | ||||
-rw-r--r-- | bin/src/utils.rs | 24 | ||||
-rw-r--r-- | lib/src/lib.rs | 19 |
12 files changed, 198 insertions, 86 deletions
@@ -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]] |
617 | name = "toml" | ||
618 | version = "0.5.8" | ||
619 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
620 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" | ||
621 | dependencies = [ | ||
622 | "serde", | ||
623 | ] | ||
624 | |||
625 | [[package]] | ||
616 | name = "unicase" | 626 | name = "unicase" |
617 | version = "2.6.0" | 627 | version = "2.6.0" |
618 | source = "registry+https://github.com/rust-lang/crates.io-index" | 628 | source = "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" | |||
23 | similar = "2.1.0" | 23 | similar = "2.1.0" |
24 | vfs = { path = "../vfs" } | 24 | vfs = { path = "../vfs" } |
25 | lib = { path = "../lib" } | 25 | lib = { path = "../lib" } |
26 | 26 | toml = "0.5.8" | |
27 | [dependencies.serde_json] | ||
28 | version = "1.0.68" | ||
29 | optional = true | ||
30 | 27 | ||
31 | [dependencies.serde] | 28 | [dependencies.serde] |
32 | version = "1.0.68" | 29 | version = "1.0.68" |
33 | features = [ "derive" ] | 30 | features = [ "derive" ] |
31 | |||
32 | [dependencies.serde_json] | ||
33 | version = "1.0.68" | ||
34 | optional = true | 34 | optional = true |
35 | 35 | ||
36 | [dev-dependencies] | 36 | [dev-dependencies] |
@@ -38,4 +38,4 @@ insta = "1.8.0" | |||
38 | strip-ansi-escapes = "0.1.1" | 38 | strip-ansi-escapes = "0.1.1" |
39 | 39 | ||
40 | [features] | 40 | [features] |
41 | json = [ "lib/json-out", "serde_json", "serde" ] | 41 | json = [ "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 @@ | |||
1 | use std::{default::Default, fmt, fs, path::PathBuf, str::FromStr}; | 1 | use std::{ |
2 | default::Default, | ||
3 | fmt, fs, | ||
4 | path::{Path, PathBuf}, | ||
5 | str::FromStr, | ||
6 | }; | ||
2 | 7 | ||
3 | use crate::{dirs, err::ConfigErr}; | 8 | use crate::{dirs, err::ConfigErr, utils, LintMap}; |
4 | 9 | ||
5 | use clap::Parser; | 10 | use clap::Parser; |
11 | use lib::LINTS; | ||
12 | use serde::{Deserialize, Serialize}; | ||
6 | use vfs::ReadOnlyVfs; | 13 | use 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)] | ||
208 | pub enum OutFormat { | ||
209 | #[cfg(feature = "json")] | ||
210 | Json, | ||
211 | Errfmt, | ||
212 | StdErr, | ||
213 | } | ||
214 | |||
215 | impl Default for OutFormat { | ||
216 | fn default() -> Self { | ||
217 | OutFormat::StdErr | ||
218 | } | ||
219 | } | ||
220 | |||
221 | impl 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 | |||
236 | impl 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)] | ||
253 | pub struct ConfFile { | ||
254 | checks: Vec<String>, | ||
255 | } | ||
256 | |||
257 | impl 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 | |||
184 | fn parse_line_col(src: &str) -> Result<(usize, usize), ConfigErr> { | 273 | fn 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)] | 317 | fn lints(conf_path: Option<&PathBuf>) -> Result<LintMap, ConfigErr> { |
229 | pub 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() | |
236 | impl 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 | |||
242 | impl 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 | |||
257 | impl 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 @@ | |||
1 | use crate::err::ExplainErr; | 1 | use crate::{err::ExplainErr, utils}; |
2 | |||
3 | use lib::LINTS; | ||
4 | 2 | ||
5 | pub fn explain(code: u32) -> Result<&'static str, ExplainErr> { | 3 | pub 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 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use crate::LintMap; | ||
4 | |||
3 | use rnix::TextRange; | 5 | use rnix::TextRange; |
4 | 6 | ||
5 | mod all; | 7 | mod all; |
6 | use all::all; | 8 | use all::all_with; |
7 | 9 | ||
8 | mod single; | 10 | mod single; |
9 | use single::single; | 11 | use single::single; |
10 | 12 | ||
11 | type Source<'a> = Cow<'a, str>; | 13 | type Source<'a> = Cow<'a, str>; |
12 | 14 | ||
13 | #[derive(Debug)] | ||
14 | pub struct FixResult<'a> { | 15 | pub 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 | ||
25 | impl<'a> FixResult<'a> { | 27 | impl<'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 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use lib::{Report, LINTS}; | 3 | use lib::Report; |
4 | use rnix::{parser::ParseError as RnixParseErr, WalkEvent}; | 4 | use rnix::{parser::ParseError as RnixParseErr, WalkEvent}; |
5 | 5 | ||
6 | use crate::fix::{FixResult, Fixed}; | 6 | use crate::{ |
7 | fix::{FixResult, Fixed}, | ||
8 | LintMap, | ||
9 | }; | ||
7 | 10 | ||
8 | fn collect_fixes(source: &str) -> Result<Vec<Report>, RnixParseErr> { | 11 | fn 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> { | |||
54 | impl<'a> Iterator for FixResult<'a> { | 57 | impl<'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 | ||
81 | pub fn all(src: &str) -> Option<FixResult> { | 85 | pub 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 @@ | |||
1 | use std::{borrow::Cow, convert::TryFrom}; | 1 | use std::{borrow::Cow, convert::TryFrom}; |
2 | 2 | ||
3 | use lib::{Report, LINTS}; | 3 | use lib::Report; |
4 | use rnix::{TextSize, WalkEvent}; | 4 | use rnix::{TextSize, WalkEvent}; |
5 | 5 | ||
6 | use crate::err::SingleFixErr; | 6 | use crate::{err::SingleFixErr, fix::Source, utils}; |
7 | use crate::fix::Source; | ||
8 | 7 | ||
9 | pub struct SingleFixResult<'δ> { | 8 | pub 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 | |||
31 | fn find(offset: TextSize, src: &str) -> Result<Report, SingleFixErr> { | 30 | fn 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; | |||
5 | pub mod fix; | 5 | pub mod fix; |
6 | pub mod lint; | 6 | pub mod lint; |
7 | pub mod traits; | 7 | pub mod traits; |
8 | |||
9 | mod utils; | ||
10 | |||
11 | use std::collections::HashMap; | ||
12 | |||
13 | use lib::Lint; | ||
14 | use rnix::SyntaxKind; | ||
15 | |||
16 | pub 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 @@ | |||
1 | use lib::{Report, LINTS}; | 1 | use crate::{utils, LintMap}; |
2 | |||
3 | use lib::Report; | ||
2 | use rnix::WalkEvent; | 4 | use rnix::WalkEvent; |
3 | use vfs::{FileId, VfsEntry}; | 5 | use 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 | ||
11 | pub fn lint(vfs_entry: VfsEntry) -> LintResult { | 13 | pub 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 | ||
38 | pub fn lint(vfs_entry: VfsEntry) -> LintResult { | ||
39 | lint_with(vfs_entry, &utils::lint_map()) | ||
40 | } | ||
41 | |||
37 | pub mod main { | 42 | pub 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 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
3 | use lib::{Lint, LINTS}; | ||
4 | use rnix::SyntaxKind; | ||
5 | |||
6 | pub 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 | |||
22 | pub 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] |
258 | macro_rules! lint_map { | 258 | macro_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 | } |