diff options
-rw-r--r-- | bin/src/config.rs | 9 | ||||
-rw-r--r-- | bin/src/err.rs | 22 | ||||
-rw-r--r-- | bin/src/lint.rs | 22 | ||||
-rw-r--r-- | bin/src/main.rs | 14 | ||||
-rw-r--r-- | bin/src/traits.rs | 18 | ||||
-rw-r--r-- | lib/src/lib.rs | 45 |
6 files changed, 89 insertions, 41 deletions
diff --git a/bin/src/config.rs b/bin/src/config.rs index cbb1f66..ef3a29d 100644 --- a/bin/src/config.rs +++ b/bin/src/config.rs | |||
@@ -190,9 +190,12 @@ fn walk_with_ignores<P: AsRef<Path>>( | |||
190 | fn vfs(files: Vec<PathBuf>) -> Result<ReadOnlyVfs, ConfigErr> { | 190 | fn vfs(files: Vec<PathBuf>) -> Result<ReadOnlyVfs, ConfigErr> { |
191 | let mut vfs = ReadOnlyVfs::default(); | 191 | let mut vfs = ReadOnlyVfs::default(); |
192 | for file in files.iter() { | 192 | for file in files.iter() { |
193 | let _id = vfs.alloc_file_id(&file); | 193 | if let Ok(data) = fs::read_to_string(&file) { |
194 | let data = fs::read_to_string(&file).map_err(ConfigErr::InvalidPath)?; | 194 | let _id = vfs.alloc_file_id(&file); |
195 | vfs.set_file_contents(&file, data.as_bytes()); | 195 | vfs.set_file_contents(&file, data.as_bytes()); |
196 | } else { | ||
197 | println!("{} contains non-utf8 content", file.display()); | ||
198 | }; | ||
196 | } | 199 | } |
197 | Ok(vfs) | 200 | Ok(vfs) |
198 | } | 201 | } |
diff --git a/bin/src/err.rs b/bin/src/err.rs index 53c3222..4c16d69 100644 --- a/bin/src/err.rs +++ b/bin/src/err.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::{io, path::PathBuf}; | 1 | use std::io; |
2 | 2 | ||
3 | use globset::ErrorKind; | 3 | use globset::ErrorKind; |
4 | use rnix::parser::ParseError; | 4 | // use rnix::parser::ParseError; |
5 | use thiserror::Error; | 5 | use thiserror::Error; |
6 | 6 | ||
7 | #[derive(Error, Debug)] | 7 | #[derive(Error, Debug)] |
@@ -14,16 +14,16 @@ pub enum ConfigErr { | |||
14 | InvalidPosition(String), | 14 | InvalidPosition(String), |
15 | } | 15 | } |
16 | 16 | ||
17 | #[derive(Error, Debug)] | 17 | // #[derive(Error, Debug)] |
18 | pub enum LintErr { | 18 | // pub enum LintErr { |
19 | #[error("[{0}] syntax error: {1}")] | 19 | // #[error("[{0}] syntax error: {1}")] |
20 | Parse(PathBuf, ParseError), | 20 | // Parse(PathBuf, ParseError), |
21 | } | 21 | // } |
22 | 22 | ||
23 | #[derive(Error, Debug)] | 23 | #[derive(Error, Debug)] |
24 | pub enum FixErr { | 24 | pub enum FixErr { |
25 | #[error("[{0}] syntax error: {1}")] | 25 | // #[error("[{0}] syntax error: {1}")] |
26 | Parse(PathBuf, ParseError), | 26 | // Parse(PathBuf, ParseError), |
27 | #[error("path error: {0}")] | 27 | #[error("path error: {0}")] |
28 | InvalidPath(#[from] io::Error), | 28 | InvalidPath(#[from] io::Error), |
29 | } | 29 | } |
@@ -42,8 +42,8 @@ pub enum SingleFixErr { | |||
42 | 42 | ||
43 | #[derive(Error, Debug)] | 43 | #[derive(Error, Debug)] |
44 | pub enum StatixErr { | 44 | pub enum StatixErr { |
45 | #[error("linter error: {0}")] | 45 | // #[error("linter error: {0}")] |
46 | Lint(#[from] LintErr), | 46 | // Lint(#[from] LintErr), |
47 | #[error("fixer error: {0}")] | 47 | #[error("fixer error: {0}")] |
48 | Fix(#[from] FixErr), | 48 | Fix(#[from] FixErr), |
49 | #[error("single fix error: {0}")] | 49 | #[error("single fix error: {0}")] |
diff --git a/bin/src/lint.rs b/bin/src/lint.rs index 65ae824..e889d31 100644 --- a/bin/src/lint.rs +++ b/bin/src/lint.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | use crate::err::LintErr; | ||
2 | |||
3 | use lib::{Report, LINTS}; | 1 | use lib::{Report, LINTS}; |
4 | use rnix::WalkEvent; | 2 | use rnix::WalkEvent; |
5 | use vfs::{FileId, VfsEntry}; | 3 | use vfs::{FileId, VfsEntry}; |
@@ -10,11 +8,16 @@ pub struct LintResult { | |||
10 | pub reports: Vec<Report>, | 8 | pub reports: Vec<Report>, |
11 | } | 9 | } |
12 | 10 | ||
13 | pub fn lint(vfs_entry: VfsEntry) -> Result<LintResult, LintErr> { | 11 | pub fn lint(vfs_entry: VfsEntry) -> LintResult { |
12 | let file_id = vfs_entry.file_id; | ||
14 | let source = vfs_entry.contents; | 13 | let source = vfs_entry.contents; |
15 | let parsed = rnix::parse(source) | 14 | let parsed = rnix::parse(source); |
16 | .as_result() | 15 | |
17 | .map_err(|e| LintErr::Parse(vfs_entry.file_path.to_path_buf(), e))?; | 16 | let error_reports = parsed |
17 | .errors() | ||
18 | .into_iter() | ||
19 | .map(|e| Report::from_parse_err(e)); | ||
20 | |||
18 | let reports = parsed | 21 | let reports = parsed |
19 | .node() | 22 | .node() |
20 | .preorder_with_tokens() | 23 | .preorder_with_tokens() |
@@ -28,9 +31,8 @@ pub fn lint(vfs_entry: VfsEntry) -> Result<LintResult, LintErr> { | |||
28 | _ => None, | 31 | _ => None, |
29 | }) | 32 | }) |
30 | .flatten() | 33 | .flatten() |
34 | .chain(error_reports) | ||
31 | .collect(); | 35 | .collect(); |
32 | Ok(LintResult { | 36 | |
33 | file_id: vfs_entry.file_id, | 37 | LintResult { file_id, reports } |
34 | reports, | ||
35 | }) | ||
36 | } | 38 | } |
diff --git a/bin/src/main.rs b/bin/src/main.rs index c5d0626..90b79ce 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs | |||
@@ -20,17 +20,9 @@ fn _main() -> Result<(), StatixErr> { | |||
20 | match opts.cmd { | 20 | match opts.cmd { |
21 | SubCommand::Check(check_config) => { | 21 | SubCommand::Check(check_config) => { |
22 | let vfs = check_config.vfs()?; | 22 | let vfs = check_config.vfs()?; |
23 | let (lints, errors): (Vec<_>, Vec<_>) = | 23 | let mut stderr = io::stderr(); |
24 | vfs.iter().map(lint::lint).partition(Result::is_ok); | 24 | vfs.iter().map(lint::lint).for_each(|r| { |
25 | let lint_results = lints.into_iter().map(Result::unwrap); | 25 | stderr.write(&r, &vfs, check_config.format).unwrap(); |
26 | let errors = errors.into_iter().map(Result::unwrap_err); | ||
27 | |||
28 | let mut stdout = io::stdout(); | ||
29 | lint_results.for_each(|r| { | ||
30 | stdout.write(&r, &vfs, check_config.format).unwrap(); | ||
31 | }); | ||
32 | errors.for_each(|e| { | ||
33 | eprintln!("{}", e); | ||
34 | }); | 26 | }); |
35 | } | 27 | } |
36 | SubCommand::Fix(fix_config) => { | 28 | SubCommand::Fix(fix_config) => { |
diff --git a/bin/src/traits.rs b/bin/src/traits.rs index 465abe4..a8ec70f 100644 --- a/bin/src/traits.rs +++ b/bin/src/traits.rs | |||
@@ -9,6 +9,7 @@ use ariadne::{ | |||
9 | CharSet, Color, Config as CliConfig, Fmt, Label, LabelAttach, Report as CliReport, | 9 | CharSet, Color, Config as CliConfig, Fmt, Label, LabelAttach, Report as CliReport, |
10 | ReportKind as CliReportKind, Source, | 10 | ReportKind as CliReportKind, Source, |
11 | }; | 11 | }; |
12 | use lib::Severity; | ||
12 | use rnix::{TextRange, TextSize}; | 13 | use rnix::{TextRange, TextSize}; |
13 | use vfs::ReadOnlyVfs; | 14 | use vfs::ReadOnlyVfs; |
14 | 15 | ||
@@ -57,11 +58,16 @@ fn write_stderr<T: Write>( | |||
57 | .map(|d| d.at.start().into()) | 58 | .map(|d| d.at.start().into()) |
58 | .min() | 59 | .min() |
59 | .unwrap_or(0usize); | 60 | .unwrap_or(0usize); |
61 | let report_kind = match report.severity { | ||
62 | Severity::Warn => CliReportKind::Warning, | ||
63 | Severity::Error => CliReportKind::Error, | ||
64 | Severity::Hint => CliReportKind::Advice, | ||
65 | }; | ||
60 | report | 66 | report |
61 | .diagnostics | 67 | .diagnostics |
62 | .iter() | 68 | .iter() |
63 | .fold( | 69 | .fold( |
64 | CliReport::build(CliReportKind::Warning, src_id, offset) | 70 | CliReport::build(report_kind, src_id, offset) |
65 | .with_config( | 71 | .with_config( |
66 | CliConfig::default() | 72 | CliConfig::default() |
67 | .with_cross_gap(true) | 73 | .with_cross_gap(true) |
@@ -103,7 +109,11 @@ fn write_errfmt<T: Write>( | |||
103 | filename = path.to_str().unwrap_or("<unknown>"), | 109 | filename = path.to_str().unwrap_or("<unknown>"), |
104 | linenumber = line, | 110 | linenumber = line, |
105 | columnnumber = col, | 111 | columnnumber = col, |
106 | errortype = "W", | 112 | errortype = match report.severity { |
113 | Severity::Warn => "W", | ||
114 | Severity::Error => "E", | ||
115 | Severity::Hint => "I", /* "info" message */ | ||
116 | }, | ||
107 | errornumber = report.code, | 117 | errornumber = report.code, |
108 | errormessage = diagnostic.message | 118 | errormessage = diagnostic.message |
109 | )?; | 119 | )?; |
@@ -118,6 +128,7 @@ mod json { | |||
118 | 128 | ||
119 | use std::io::{self, Write}; | 129 | use std::io::{self, Write}; |
120 | 130 | ||
131 | use lib::Severity; | ||
121 | use rnix::TextRange; | 132 | use rnix::TextRange; |
122 | use serde::Serialize; | 133 | use serde::Serialize; |
123 | use serde_json; | 134 | use serde_json; |
@@ -134,6 +145,7 @@ mod json { | |||
134 | struct JsonReport<'μ> { | 145 | struct JsonReport<'μ> { |
135 | note: &'static str, | 146 | note: &'static str, |
136 | code: u32, | 147 | code: u32, |
148 | severity: &'μ Severity, | ||
137 | diagnostics: Vec<JsonDiagnostic<'μ>>, | 149 | diagnostics: Vec<JsonDiagnostic<'μ>>, |
138 | } | 150 | } |
139 | 151 | ||
@@ -192,6 +204,7 @@ mod json { | |||
192 | .map(|r| { | 204 | .map(|r| { |
193 | let note = r.note; | 205 | let note = r.note; |
194 | let code = r.code; | 206 | let code = r.code; |
207 | let severity = &r.severity; | ||
195 | let diagnostics = r | 208 | let diagnostics = r |
196 | .diagnostics | 209 | .diagnostics |
197 | .iter() | 210 | .iter() |
@@ -207,6 +220,7 @@ mod json { | |||
207 | JsonReport { | 220 | JsonReport { |
208 | note, | 221 | note, |
209 | code, | 222 | code, |
223 | severity, | ||
210 | diagnostics, | 224 | diagnostics, |
211 | } | 225 | } |
212 | }) | 226 | }) |
diff --git a/lib/src/lib.rs b/lib/src/lib.rs index ec96f50..196cbf8 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs | |||
@@ -4,7 +4,7 @@ mod make; | |||
4 | 4 | ||
5 | pub use lints::LINTS; | 5 | pub use lints::LINTS; |
6 | 6 | ||
7 | use rnix::{SyntaxElement, SyntaxKind, TextRange}; | 7 | use rnix::{parser::ParseError, SyntaxElement, SyntaxKind, TextRange}; |
8 | use std::{convert::Into, default::Default}; | 8 | use std::{convert::Into, default::Default}; |
9 | 9 | ||
10 | #[cfg(feature = "json-out")] | 10 | #[cfg(feature = "json-out")] |
@@ -13,10 +13,18 @@ use serde::{ | |||
13 | Serialize, | 13 | Serialize, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | #[derive(Debug)] | ||
16 | #[cfg_attr(feature = "json-out", derive(Serialize))] | 17 | #[cfg_attr(feature = "json-out", derive(Serialize))] |
17 | pub struct Position { | 18 | pub enum Severity { |
18 | line: usize, | 19 | Warn, |
19 | column: usize, | 20 | Error, |
21 | Hint, | ||
22 | } | ||
23 | |||
24 | impl Default for Severity { | ||
25 | fn default() -> Self { | ||
26 | Self::Warn | ||
27 | } | ||
20 | } | 28 | } |
21 | 29 | ||
22 | /// Report generated by a lint | 30 | /// Report generated by a lint |
@@ -27,6 +35,8 @@ pub struct Report { | |||
27 | pub note: &'static str, | 35 | pub note: &'static str, |
28 | /// An error code to uniquely identify this lint | 36 | /// An error code to uniquely identify this lint |
29 | pub code: u32, | 37 | pub code: u32, |
38 | /// Report severity level | ||
39 | pub severity: Severity, | ||
30 | /// Collection of diagnostics raised by this lint | 40 | /// Collection of diagnostics raised by this lint |
31 | pub diagnostics: Vec<Diagnostic>, | 41 | pub diagnostics: Vec<Diagnostic>, |
32 | } | 42 | } |
@@ -56,6 +66,11 @@ impl Report { | |||
56 | .push(Diagnostic::suggest(at, message, suggestion)); | 66 | .push(Diagnostic::suggest(at, message, suggestion)); |
57 | self | 67 | self |
58 | } | 68 | } |
69 | /// Set severity level | ||
70 | pub fn severity(mut self, severity: Severity) -> Self { | ||
71 | self.severity = severity; | ||
72 | self | ||
73 | } | ||
59 | /// A range that encompasses all the suggestions provided in this report | 74 | /// A range that encompasses all the suggestions provided in this report |
60 | pub fn total_suggestion_range(&self) -> Option<TextRange> { | 75 | pub fn total_suggestion_range(&self) -> Option<TextRange> { |
61 | self.diagnostics | 76 | self.diagnostics |
@@ -80,6 +95,28 @@ impl Report { | |||
80 | d.apply(src); | 95 | d.apply(src); |
81 | } | 96 | } |
82 | } | 97 | } |
98 | /// Create a report out of a parse error | ||
99 | pub fn from_parse_err(err: ParseError) -> Self { | ||
100 | let at = match err { | ||
101 | ParseError::Unexpected(at) => at, | ||
102 | ParseError::UnexpectedExtra(at) => at, | ||
103 | ParseError::UnexpectedWanted(_, at, _) => at, | ||
104 | ParseError::UnexpectedDoubleBind(at) => at, | ||
105 | ParseError::UnexpectedEOF | ParseError::UnexpectedEOFWanted(_) => { | ||
106 | TextRange::empty(0u32.into()) | ||
107 | } | ||
108 | _ => panic!("report a bug, pepper forgot to handle a parse error"), | ||
109 | }; | ||
110 | let mut message = err.to_string(); | ||
111 | message | ||
112 | .as_mut_str() | ||
113 | .get_mut(0..1) | ||
114 | .unwrap() | ||
115 | .make_ascii_uppercase(); | ||
116 | Self::new("Syntax error", 0) | ||
117 | .diagnostic(at, message) | ||
118 | .severity(Severity::Error) | ||
119 | } | ||
83 | } | 120 | } |
84 | 121 | ||
85 | /// Mapping from a bytespan to an error message. | 122 | /// Mapping from a bytespan to an error message. |