diff options
author | Akshay <[email protected]> | 2021-10-28 12:44:21 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2021-10-28 12:44:21 +0100 |
commit | a627799e2a0989201416a11b8d3bf1af0be86073 (patch) | |
tree | 556e08c309525fb2d89136235bb925dd5577f759 | |
parent | aa1a85527613ca8cabd5b7134ab46833564439c7 (diff) |
improve json output
The `at` field now includes a `from` and `to` position with line and
column information, instead of a [usize; 2].
-rw-r--r-- | bin/src/traits.rs | 96 | ||||
-rw-r--r-- | lib/src/lib.rs | 6 | ||||
-rw-r--r-- | lib/src/lints/manual_inherit_from.rs | 1 |
3 files changed, 87 insertions, 16 deletions
diff --git a/bin/src/traits.rs b/bin/src/traits.rs index a0e40d4..5068764 100644 --- a/bin/src/traits.rs +++ b/bin/src/traits.rs | |||
@@ -9,7 +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 rnix::TextRange; | 12 | use rnix::{TextRange, TextSize}; |
13 | use vfs::ReadOnlyVfs; | 13 | use vfs::ReadOnlyVfs; |
14 | 14 | ||
15 | pub trait WriteDiagnostic { | 15 | pub trait WriteDiagnostic { |
@@ -95,8 +95,8 @@ fn write_errfmt<T: Write>( | |||
95 | let path = vfs.file_path(file_id); | 95 | let path = vfs.file_path(file_id); |
96 | for report in lint_result.reports.iter() { | 96 | for report in lint_result.reports.iter() { |
97 | for diagnostic in report.diagnostics.iter() { | 97 | for diagnostic in report.diagnostics.iter() { |
98 | let line = line(diagnostic.at, &src); | 98 | let line = line(diagnostic.at.start(), &src); |
99 | let col = column(diagnostic.at, &src); | 99 | let col = column(diagnostic.at.start(), &src); |
100 | writeln!( | 100 | writeln!( |
101 | writer, | 101 | writer, |
102 | "{filename}>{linenumber}:{columnnumber}:{errortype}:{errornumber}:{errormessage}", | 102 | "{filename}>{linenumber}:{columnnumber}:{errortype}:{errornumber}:{errormessage}", |
@@ -115,17 +115,62 @@ fn write_errfmt<T: Write>( | |||
115 | #[cfg(feature = "json")] | 115 | #[cfg(feature = "json")] |
116 | mod json { | 116 | mod json { |
117 | use crate::lint::LintResult; | 117 | use crate::lint::LintResult; |
118 | |||
118 | use std::io::{self, Write}; | 119 | use std::io::{self, Write}; |
119 | use vfs::ReadOnlyVfs; | ||
120 | 120 | ||
121 | use lib::Report; | 121 | use lib::Suggestion; |
122 | use rnix::TextRange; | ||
122 | use serde::Serialize; | 123 | use serde::Serialize; |
123 | use serde_json; | 124 | use serde_json; |
125 | use vfs::ReadOnlyVfs; | ||
126 | |||
127 | #[derive(Serialize)] | ||
128 | struct Out<'μ> { | ||
129 | #[serde(rename = "file")] | ||
130 | path: &'μ std::path::Path, | ||
131 | report: Vec<JsonReport<'μ>>, | ||
132 | } | ||
124 | 133 | ||
125 | #[derive(Serialize)] | 134 | #[derive(Serialize)] |
126 | struct JsonReport<'μ> { | 135 | struct JsonReport<'μ> { |
127 | file: &'μ std::path::Path, | 136 | note: &'static str, |
128 | report: Vec<&'μ Report>, | 137 | code: u32, |
138 | diagnostics: Vec<JsonDiagnostic<'μ>>, | ||
139 | } | ||
140 | |||
141 | #[derive(Serialize)] | ||
142 | struct JsonDiagnostic<'μ> { | ||
143 | at: JsonSpan, | ||
144 | message: &'μ String, | ||
145 | suggestion: &'μ Option<Suggestion>, | ||
146 | } | ||
147 | |||
148 | #[derive(Serialize)] | ||
149 | struct JsonSpan { | ||
150 | from: Position, | ||
151 | to: Position, | ||
152 | } | ||
153 | |||
154 | #[derive(Serialize)] | ||
155 | struct Position { | ||
156 | line: usize, | ||
157 | column: usize, | ||
158 | } | ||
159 | |||
160 | impl JsonSpan { | ||
161 | fn from_textrange(at: TextRange, src: &str) -> Self { | ||
162 | let start = at.start(); | ||
163 | let end = at.end(); | ||
164 | let from = Position { | ||
165 | line: super::line(start, src), | ||
166 | column: super::column(start, src), | ||
167 | }; | ||
168 | let to = Position { | ||
169 | line: super::line(end, src), | ||
170 | column: super::column(end, src), | ||
171 | }; | ||
172 | Self { from, to } | ||
173 | } | ||
129 | } | 174 | } |
130 | 175 | ||
131 | pub fn write_json<T: Write>( | 176 | pub fn write_json<T: Write>( |
@@ -135,26 +180,45 @@ mod json { | |||
135 | ) -> io::Result<()> { | 180 | ) -> io::Result<()> { |
136 | let file_id = lint_result.file_id; | 181 | let file_id = lint_result.file_id; |
137 | let path = vfs.file_path(file_id); | 182 | let path = vfs.file_path(file_id); |
183 | let src = vfs.get_str(file_id); | ||
184 | let report = lint_result | ||
185 | .reports | ||
186 | .iter() | ||
187 | .map(|r| { | ||
188 | let note = r.note; | ||
189 | let code = r.code; | ||
190 | let diagnostics = r | ||
191 | .diagnostics | ||
192 | .iter() | ||
193 | .map(|d| JsonDiagnostic { | ||
194 | at: JsonSpan::from_textrange(d.at, src), | ||
195 | message: &d.message, | ||
196 | suggestion: &d.suggestion, | ||
197 | }) | ||
198 | .collect::<Vec<_>>(); | ||
199 | JsonReport { | ||
200 | note, | ||
201 | code, | ||
202 | diagnostics, | ||
203 | } | ||
204 | }) | ||
205 | .collect(); | ||
138 | writeln!( | 206 | writeln!( |
139 | writer, | 207 | writer, |
140 | "{}", | 208 | "{}", |
141 | serde_json::to_string_pretty(&JsonReport { | 209 | serde_json::to_string_pretty(&Out { path, report }).unwrap() |
142 | file: path, | ||
143 | report: lint_result.reports.iter().collect::<Vec<_>>() | ||
144 | }) | ||
145 | .unwrap() | ||
146 | )?; | 210 | )?; |
147 | Ok(()) | 211 | Ok(()) |
148 | } | 212 | } |
149 | } | 213 | } |
150 | 214 | ||
151 | fn line(at: TextRange, src: &str) -> usize { | 215 | fn line(at: TextSize, src: &str) -> usize { |
152 | let at = at.start().into(); | 216 | let at = at.into(); |
153 | src[..at].chars().filter(|&c| c == '\n').count() + 1 | 217 | src[..at].chars().filter(|&c| c == '\n').count() + 1 |
154 | } | 218 | } |
155 | 219 | ||
156 | fn column(at: TextRange, src: &str) -> usize { | 220 | fn column(at: TextSize, src: &str) -> usize { |
157 | let at = at.start().into(); | 221 | let at = at.into(); |
158 | src[..at].rfind('\n').map(|c| at - c).unwrap_or(at + 1) | 222 | src[..at].rfind('\n').map(|c| at - c).unwrap_or(at + 1) |
159 | } | 223 | } |
160 | 224 | ||
diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 753e5c1..ec96f50 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs | |||
@@ -13,6 +13,12 @@ use serde::{ | |||
13 | Serialize, | 13 | Serialize, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | #[cfg_attr(feature = "json-out", derive(Serialize))] | ||
17 | pub struct Position { | ||
18 | line: usize, | ||
19 | column: usize, | ||
20 | } | ||
21 | |||
16 | /// Report generated by a lint | 22 | /// Report generated by a lint |
17 | #[derive(Debug, Default)] | 23 | #[derive(Debug, Default)] |
18 | #[cfg_attr(feature = "json-out", derive(Serialize))] | 24 | #[cfg_attr(feature = "json-out", derive(Serialize))] |
diff --git a/lib/src/lints/manual_inherit_from.rs b/lib/src/lints/manual_inherit_from.rs index 355ed8a..794aaf9 100644 --- a/lib/src/lints/manual_inherit_from.rs +++ b/lib/src/lints/manual_inherit_from.rs | |||
@@ -22,6 +22,7 @@ impl Rule for ManualInherit { | |||
22 | if let Some(key_value_stmt) = KeyValue::cast(node.clone()); | 22 | if let Some(key_value_stmt) = KeyValue::cast(node.clone()); |
23 | if let mut key_path = key_value_stmt.key()?.path(); | 23 | if let mut key_path = key_value_stmt.key()?.path(); |
24 | if let Some(key_node) = key_path.next(); | 24 | if let Some(key_node) = key_path.next(); |
25 | // ensure that path has exactly one component | ||
25 | if key_path.next().is_none(); | 26 | if key_path.next().is_none(); |
26 | if let Some(key) = Ident::cast(key_node); | 27 | if let Some(key) = Ident::cast(key_node); |
27 | 28 | ||