aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-10-28 12:44:21 +0100
committerAkshay <[email protected]>2021-10-28 12:44:21 +0100
commita627799e2a0989201416a11b8d3bf1af0be86073 (patch)
tree556e08c309525fb2d89136235bb925dd5577f759
parentaa1a85527613ca8cabd5b7134ab46833564439c7 (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.rs96
-rw-r--r--lib/src/lib.rs6
-rw-r--r--lib/src/lints/manual_inherit_from.rs1
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};
12use rnix::TextRange; 12use rnix::{TextRange, TextSize};
13use vfs::ReadOnlyVfs; 13use vfs::ReadOnlyVfs;
14 14
15pub trait WriteDiagnostic { 15pub 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")]
116mod json { 116mod 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
151fn line(at: TextRange, src: &str) -> usize { 215fn 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
156fn column(at: TextRange, src: &str) -> usize { 220fn 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))]
17pub 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