aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock47
-rw-r--r--bin/Cargo.toml12
-rw-r--r--bin/src/traits.rs45
-rw-r--r--lib/Cargo.toml10
-rw-r--r--lib/src/lib.rs44
5 files changed, 156 insertions, 2 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5fe64ab..a527d30 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -170,6 +170,12 @@ dependencies = [
170] 170]
171 171
172[[package]] 172[[package]]
173name = "itoa"
174version = "0.4.8"
175source = "registry+https://github.com/rust-lang/crates.io-index"
176checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
177
178[[package]]
173name = "lazy_static" 179name = "lazy_static"
174version = "1.4.0" 180version = "1.4.0"
175source = "registry+https://github.com/rust-lang/crates.io-index" 181source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -184,6 +190,8 @@ dependencies = [
184 "macros", 190 "macros",
185 "rnix", 191 "rnix",
186 "rowan", 192 "rowan",
193 "serde",
194 "serde_json",
187] 195]
188 196
189[[package]] 197[[package]]
@@ -330,6 +338,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
330checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 338checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
331 339
332[[package]] 340[[package]]
341name = "ryu"
342version = "1.0.5"
343source = "registry+https://github.com/rust-lang/crates.io-index"
344checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
345
346[[package]]
347name = "serde"
348version = "1.0.130"
349source = "registry+https://github.com/rust-lang/crates.io-index"
350checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
351dependencies = [
352 "serde_derive",
353]
354
355[[package]]
356name = "serde_derive"
357version = "1.0.130"
358source = "registry+https://github.com/rust-lang/crates.io-index"
359checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
360dependencies = [
361 "proc-macro2",
362 "quote",
363 "syn",
364]
365
366[[package]]
367name = "serde_json"
368version = "1.0.68"
369source = "registry+https://github.com/rust-lang/crates.io-index"
370checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
371dependencies = [
372 "itoa",
373 "ryu",
374 "serde",
375]
376
377[[package]]
333name = "similar" 378name = "similar"
334version = "2.1.0" 379version = "2.1.0"
335source = "registry+https://github.com/rust-lang/crates.io-index" 380source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -350,6 +395,8 @@ dependencies = [
350 "globset", 395 "globset",
351 "lib", 396 "lib",
352 "rnix", 397 "rnix",
398 "serde",
399 "serde_json",
353 "similar", 400 "similar",
354 "thiserror", 401 "thiserror",
355 "vfs", 402 "vfs",
diff --git a/bin/Cargo.toml b/bin/Cargo.toml
index af5a288..528f804 100644
--- a/bin/Cargo.toml
+++ b/bin/Cargo.toml
@@ -14,3 +14,15 @@ thiserror = "1.0.30"
14similar = "2.1.0" 14similar = "2.1.0"
15vfs = { path = "../vfs" } 15vfs = { path = "../vfs" }
16lib = { path = "../lib" } 16lib = { path = "../lib" }
17
18[dependencies.serde_json]
19version = "1.0.68"
20optional = true
21
22[dependencies.serde]
23version = "1.0.68"
24features = [ "derive" ]
25optional = true
26
27[features]
28json = [ "lib/json-out", "serde_json", "serde" ]
diff --git a/bin/src/traits.rs b/bin/src/traits.rs
index 5cf7208..a0e40d4 100644
--- a/bin/src/traits.rs
+++ b/bin/src/traits.rs
@@ -32,9 +32,10 @@ where
32 format: OutFormat, 32 format: OutFormat,
33 ) -> io::Result<()> { 33 ) -> io::Result<()> {
34 match format { 34 match format {
35 #[cfg(feature = "json")]
36 OutFormat::Json => json::write_json(self, lint_result, vfs),
35 OutFormat::StdErr => write_stderr(self, lint_result, vfs), 37 OutFormat::StdErr => write_stderr(self, lint_result, vfs),
36 OutFormat::Errfmt => write_errfmt(self, lint_result, vfs), 38 OutFormat::Errfmt => write_errfmt(self, lint_result, vfs),
37 _ => Ok(()),
38 } 39 }
39 } 40 }
40} 41}
@@ -84,7 +85,11 @@ fn write_stderr<T: Write>(
84 Ok(()) 85 Ok(())
85} 86}
86 87
87fn write_errfmt<T: Write>(writer: &mut T, lint_result: &LintResult, vfs: &ReadOnlyVfs) -> io::Result<()> { 88fn write_errfmt<T: Write>(
89 writer: &mut T,
90 lint_result: &LintResult,
91 vfs: &ReadOnlyVfs,
92) -> io::Result<()> {
88 let file_id = lint_result.file_id; 93 let file_id = lint_result.file_id;
89 let src = str::from_utf8(vfs.get(file_id)).unwrap(); 94 let src = str::from_utf8(vfs.get(file_id)).unwrap();
90 let path = vfs.file_path(file_id); 95 let path = vfs.file_path(file_id);
@@ -107,6 +112,42 @@ fn write_errfmt<T: Write>(writer: &mut T, lint_result: &LintResult, vfs: &ReadOn
107 Ok(()) 112 Ok(())
108} 113}
109 114
115#[cfg(feature = "json")]
116mod json {
117 use crate::lint::LintResult;
118 use std::io::{self, Write};
119 use vfs::ReadOnlyVfs;
120
121 use lib::Report;
122 use serde::Serialize;
123 use serde_json;
124
125 #[derive(Serialize)]
126 struct JsonReport<'μ> {
127 file: &'μ std::path::Path,
128 report: Vec<&'μ Report>,
129 }
130
131 pub fn write_json<T: Write>(
132 writer: &mut T,
133 lint_result: &LintResult,
134 vfs: &ReadOnlyVfs,
135 ) -> io::Result<()> {
136 let file_id = lint_result.file_id;
137 let path = vfs.file_path(file_id);
138 writeln!(
139 writer,
140 "{}",
141 serde_json::to_string_pretty(&JsonReport {
142 file: path,
143 report: lint_result.reports.iter().collect::<Vec<_>>()
144 })
145 .unwrap()
146 )?;
147 Ok(())
148 }
149}
150
110fn line(at: TextRange, src: &str) -> usize { 151fn line(at: TextRange, src: &str) -> usize {
111 let at = at.start().into(); 152 let at = at.start().into();
112 src[..at].chars().filter(|&c| c == '\n').count() + 1 153 src[..at].chars().filter(|&c| c == '\n').count() + 1
diff --git a/lib/Cargo.toml b/lib/Cargo.toml
index e9e9e69..e9bca06 100644
--- a/lib/Cargo.toml
+++ b/lib/Cargo.toml
@@ -11,3 +11,13 @@ if_chain = "1.0"
11macros = { path = "../macros" } 11macros = { path = "../macros" }
12lazy_static = "1.0" 12lazy_static = "1.0"
13rowan = "0.12.5" 13rowan = "0.12.5"
14serde_json = { version = "1.0.68", optional = true }
15
16[dependencies.serde]
17version = "1.0.130"
18features = [ "derive" ]
19optional = true
20
21[features]
22default = []
23json-out = [ "serde", "serde_json" ]
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index d8a3859..bde039f 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -6,8 +6,12 @@ pub use lints::LINTS;
6use rnix::{SyntaxElement, SyntaxKind, TextRange}; 6use rnix::{SyntaxElement, SyntaxKind, TextRange};
7use std::{convert::Into, default::Default}; 7use std::{convert::Into, default::Default};
8 8
9#[cfg(feature = "json-out")]
10use serde::{Serialize, ser::{SerializeStruct, Serializer}};
11
9/// Report generated by a lint 12/// Report generated by a lint
10#[derive(Debug, Default)] 13#[derive(Debug, Default)]
14#[cfg_attr(feature = "json-out", derive(Serialize))]
11pub struct Report { 15pub struct Report {
12 /// General information about this lint and where it applies. 16 /// General information about this lint and where it applies.
13 pub note: &'static str, 17 pub note: &'static str,
@@ -95,6 +99,27 @@ impl Diagnostic {
95 } 99 }
96} 100}
97 101
102#[cfg(feature = "json-out")]
103impl Serialize for Diagnostic {
104 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
105 where
106 S: Serializer,
107 {
108 let mut s = serializer.serialize_struct("Diagnostic", 3)?;
109 let at = {
110 let start = usize::from(self.at.start());
111 let end = usize::from(self.at.end());
112 (start, end)
113 };
114 s.serialize_field("at", &at)?;
115 s.serialize_field("message", &self.message)?;
116 if let Some(suggestion) = &self.suggestion {
117 s.serialize_field("suggestion", suggestion)?;
118 }
119 s.end()
120 }
121}
122
98/// Suggested fix for a diagnostic, the fix is provided as a syntax element. 123/// Suggested fix for a diagnostic, the fix is provided as a syntax element.
99/// Look at `make.rs` to construct fixes. 124/// Look at `make.rs` to construct fixes.
100#[derive(Debug)] 125#[derive(Debug)]
@@ -119,6 +144,25 @@ impl Suggestion {
119 } 144 }
120} 145}
121 146
147#[cfg(feature = "json-out")]
148impl Serialize for Suggestion {
149 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150 where
151 S: Serializer,
152 {
153 let mut s = serializer.serialize_struct("Suggestion", 2)?;
154 let at = {
155 let start = usize::from(self.at.start());
156 let end = usize::from(self.at.end());
157 (start, end)
158 };
159 let fix = self.fix.to_string();
160 s.serialize_field("at", &at)?;
161 s.serialize_field("fix", &fix)?;
162 s.end()
163 }
164}
165
122/// Lint logic is defined via this trait. Do not implement manually, 166/// Lint logic is defined via this trait. Do not implement manually,
123/// look at the `lint` attribute macro instead for implementing rules 167/// look at the `lint` attribute macro instead for implementing rules
124pub trait Rule { 168pub trait Rule {