From 35c30356a22635d93c62a4411044cae34fd279d6 Mon Sep 17 00:00:00 2001 From: Tobias Fischer Date: Sat, 1 Jun 2024 16:17:05 +0200 Subject: [PATCH 1/7] output(RDJson): implement diagnostic output --- crates/ruff/src/printer.rs | 6 +- crates/ruff_linter/src/message/mod.rs | 2 + crates/ruff_linter/src/message/rdjson.rs | 88 ++++++++++++++++++++++++ crates/ruff_linter/src/settings/types.rs | 2 + 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 crates/ruff_linter/src/message/rdjson.rs diff --git a/crates/ruff/src/printer.rs b/crates/ruff/src/printer.rs index c44150602bdce..1d2a18569b786 100644 --- a/crates/ruff/src/printer.rs +++ b/crates/ruff/src/printer.rs @@ -13,7 +13,8 @@ use ruff_linter::fs::relativize_path; use ruff_linter::logging::LogLevel; use ruff_linter::message::{ AzureEmitter, Emitter, EmitterContext, GithubEmitter, GitlabEmitter, GroupedEmitter, - JsonEmitter, JsonLinesEmitter, JunitEmitter, PylintEmitter, SarifEmitter, TextEmitter, + JsonEmitter, JsonLinesEmitter, JunitEmitter, PylintEmitter, RDJsonEmitter, SarifEmitter, + TextEmitter, }; use ruff_linter::notify_user; use ruff_linter::registry::{AsRule, Rule}; @@ -242,6 +243,9 @@ impl Printer { SerializationFormat::Json => { JsonEmitter.emit(writer, &diagnostics.messages, &context)?; } + SerializationFormat::RDJson => { + RDJsonEmitter.emit(writer, &diagnostics.messages, &context)?; + } SerializationFormat::JsonLines => { JsonLinesEmitter.emit(writer, &diagnostics.messages, &context)?; } diff --git a/crates/ruff_linter/src/message/mod.rs b/crates/ruff_linter/src/message/mod.rs index 2f44de44eda71..02cd08e1a43c4 100644 --- a/crates/ruff_linter/src/message/mod.rs +++ b/crates/ruff_linter/src/message/mod.rs @@ -13,6 +13,7 @@ pub use json::JsonEmitter; pub use json_lines::JsonLinesEmitter; pub use junit::JunitEmitter; pub use pylint::PylintEmitter; +pub use rdjson::RDJsonEmitter; use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix}; use ruff_notebook::NotebookIndex; use ruff_source_file::{SourceFile, SourceLocation}; @@ -29,6 +30,7 @@ mod json; mod json_lines; mod junit; mod pylint; +mod rdjson; mod sarif; mod text; diff --git a/crates/ruff_linter/src/message/rdjson.rs b/crates/ruff_linter/src/message/rdjson.rs new file mode 100644 index 0000000000000..4bb12bcb41f7e --- /dev/null +++ b/crates/ruff_linter/src/message/rdjson.rs @@ -0,0 +1,88 @@ +use std::io::Write; + +use serde::ser::SerializeSeq; +use serde::{Serialize, Serializer}; +use serde_json::{json, Value}; + +use ruff_text_size::Ranged; + +use crate::message::{Emitter, EmitterContext, Message, SourceLocation}; +use crate::registry::AsRule; + +#[derive(Default)] +pub struct RDJsonEmitter; + +impl Emitter for RDJsonEmitter { + fn emit( + &mut self, + writer: &mut dyn Write, + messages: &[Message], + _context: &EmitterContext, + ) -> anyhow::Result<()> { + serde_json::to_writer_pretty( + writer, + &json!({ + "source": { + "name": "ruff", + "url": "https://docs.astral.sh/ruff", + }, + "severity": "warning", + "diagnostics": &ExpandedMessages{ messages } + }), + )?; + + Ok(()) + } +} + +struct ExpandedMessages<'a> { + messages: &'a [Message], +} + +impl Serialize for ExpandedMessages<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_seq(Some(self.messages.len()))?; + + for message in self.messages { + let value = message_to_rdjson_value(message); + s.serialize_element(&value)?; + } + + s.end() + } +} + +pub(crate) fn message_to_rdjson_value(message: &Message) -> Value { + let source_code = message.file.to_source_code(); + + let start_location = source_code.source_location(message.start()); + let end_location = source_code.source_location(message.end()); + + json!({ + "message": message.kind.body, + "location": { + "path": message.filename(), + "range": rdjson_range(start_location, end_location), + }, + "code": { + "value": message.kind.rule().noqa_code().to_string(), + "url": message.kind.rule().url(), + }, + }) +} + +fn rdjson_range(start: SourceLocation, end: SourceLocation) -> Value { + json!({ + "start": { + "line": start.row, + "column": start.column, + }, + "end": { + "line": start.row, + "column": end.column, + }, + }) +} diff --git a/crates/ruff_linter/src/settings/types.rs b/crates/ruff_linter/src/settings/types.rs index ce9f9abd057f8..ae8770fe96ca1 100644 --- a/crates/ruff_linter/src/settings/types.rs +++ b/crates/ruff_linter/src/settings/types.rs @@ -513,6 +513,7 @@ pub enum SerializationFormat { Github, Gitlab, Pylint, + RDJson, Azure, Sarif, } @@ -530,6 +531,7 @@ impl Display for SerializationFormat { Self::Github => write!(f, "github"), Self::Gitlab => write!(f, "gitlab"), Self::Pylint => write!(f, "pylint"), + Self::RDJson => write!(f, "rdjson"), Self::Azure => write!(f, "azure"), Self::Sarif => write!(f, "sarif"), } From de7d86cdc6d85ec4800e525ff38d15c4e1d109b8 Mon Sep 17 00:00:00 2001 From: Tobias Fischer Date: Sat, 1 Jun 2024 16:53:34 +0200 Subject: [PATCH 2/7] output(RDJson): implement fix output --- crates/ruff_linter/src/message/rdjson.rs | 36 +++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/message/rdjson.rs b/crates/ruff_linter/src/message/rdjson.rs index 4bb12bcb41f7e..8e9555b032846 100644 --- a/crates/ruff_linter/src/message/rdjson.rs +++ b/crates/ruff_linter/src/message/rdjson.rs @@ -4,6 +4,8 @@ use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; use serde_json::{json, Value}; +use ruff_diagnostics::Edit; +use ruff_source_file::SourceCode; use ruff_text_size::Ranged; use crate::message::{Emitter, EmitterContext, Message, SourceLocation}; @@ -58,6 +60,11 @@ impl Serialize for ExpandedMessages<'_> { pub(crate) fn message_to_rdjson_value(message: &Message) -> Value { let source_code = message.file.to_source_code(); + let fix = message.fix.as_ref().map(|fix| ExpandedEdits { + edits: fix.edits(), + source_code: &source_code, + }); + let start_location = source_code.source_location(message.start()); let end_location = source_code.source_location(message.end()); @@ -71,9 +78,36 @@ pub(crate) fn message_to_rdjson_value(message: &Message) -> Value { "value": message.kind.rule().noqa_code().to_string(), "url": message.kind.rule().url(), }, + "suggestions": fix, }) } +struct ExpandedEdits<'a> { + edits: &'a [Edit], + source_code: &'a SourceCode<'a, 'a>, +} + +impl Serialize for ExpandedEdits<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_seq(Some(self.edits.len()))?; + + for edit in self.edits { + let location = self.source_code.source_location(edit.start()); + let end_location = self.source_code.source_location(edit.end()); + + s.serialize_element(&json!({ + "range": rdjson_range(location, end_location), + "text": edit.content().unwrap_or_default(), + }))?; + } + + s.end() + } +} + fn rdjson_range(start: SourceLocation, end: SourceLocation) -> Value { json!({ "start": { @@ -81,7 +115,7 @@ fn rdjson_range(start: SourceLocation, end: SourceLocation) -> Value { "column": start.column, }, "end": { - "line": start.row, + "line": end.row, "column": end.column, }, }) From 97e1d1245898b5af1d56f3d481cc8fc224a51c5f Mon Sep 17 00:00:00 2001 From: Tobias Fischer Date: Sat, 1 Jun 2024 17:16:13 +0200 Subject: [PATCH 3/7] output(RDJson): add snapshot test --- crates/ruff_linter/src/message/rdjson.rs | 16 +++ ...inter__message__rdjson__tests__output.snap | 104 ++++++++++++++++++ docs/configuration.md | 2 +- ruff.schema.json | 1 + 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 crates/ruff_linter/src/message/snapshots/ruff_linter__message__rdjson__tests__output.snap diff --git a/crates/ruff_linter/src/message/rdjson.rs b/crates/ruff_linter/src/message/rdjson.rs index 8e9555b032846..dfe6c31160691 100644 --- a/crates/ruff_linter/src/message/rdjson.rs +++ b/crates/ruff_linter/src/message/rdjson.rs @@ -120,3 +120,19 @@ fn rdjson_range(start: SourceLocation, end: SourceLocation) -> Value { }, }) } + +#[cfg(test)] +mod tests { + use insta::assert_snapshot; + + use crate::message::tests::{capture_emitter_output, create_messages}; + use crate::message::RDJsonEmitter; + + #[test] + fn output() { + let mut emitter = RDJsonEmitter; + let content = capture_emitter_output(&mut emitter, &create_messages()); + + assert_snapshot!(content); + } +} diff --git a/crates/ruff_linter/src/message/snapshots/ruff_linter__message__rdjson__tests__output.snap b/crates/ruff_linter/src/message/snapshots/ruff_linter__message__rdjson__tests__output.snap new file mode 100644 index 0000000000000..13752f7b58365 --- /dev/null +++ b/crates/ruff_linter/src/message/snapshots/ruff_linter__message__rdjson__tests__output.snap @@ -0,0 +1,104 @@ +--- +source: crates/ruff_linter/src/message/rdjson.rs +expression: content +--- +{ + "diagnostics": [ + { + "code": { + "url": "https://docs.astral.sh/ruff/rules/unused-import", + "value": "F401" + }, + "location": { + "path": "fib.py", + "range": { + "end": { + "column": 10, + "line": 1 + }, + "start": { + "column": 8, + "line": 1 + } + } + }, + "message": "`os` imported but unused", + "suggestions": [ + { + "range": { + "end": { + "column": 1, + "line": 2 + }, + "start": { + "column": 1, + "line": 1 + } + }, + "text": "" + } + ] + }, + { + "code": { + "url": "https://docs.astral.sh/ruff/rules/unused-variable", + "value": "F841" + }, + "location": { + "path": "fib.py", + "range": { + "end": { + "column": 6, + "line": 6 + }, + "start": { + "column": 5, + "line": 6 + } + } + }, + "message": "Local variable `x` is assigned to but never used", + "suggestions": [ + { + "range": { + "end": { + "column": 10, + "line": 6 + }, + "start": { + "column": 5, + "line": 6 + } + }, + "text": "" + } + ] + }, + { + "code": { + "url": "https://docs.astral.sh/ruff/rules/undefined-name", + "value": "F821" + }, + "location": { + "path": "undef.py", + "range": { + "end": { + "column": 5, + "line": 1 + }, + "start": { + "column": 4, + "line": 1 + } + } + }, + "message": "Undefined name `a`", + "suggestions": null + } + ], + "severity": "warning", + "source": { + "name": "ruff", + "url": "https://docs.astral.sh/ruff" + } +} diff --git a/docs/configuration.md b/docs/configuration.md index 8f38279e25d27..b1993697db654 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -599,7 +599,7 @@ Options: format is "concise". In preview mode, the default serialization format is "full" [env: RUFF_OUTPUT_FORMAT=] [possible values: text, concise, full, json, json-lines, junit, grouped, github, gitlab, - pylint, azure, sarif] + pylint, rd-json, azure, sarif] -o, --output-file Specify file to write the linter output to (default: stdout) [env: RUFF_OUTPUT_FILE=] diff --git a/ruff.schema.json b/ruff.schema.json index b146d9b74bcc2..4216548a5c56c 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3947,6 +3947,7 @@ "github", "gitlab", "pylint", + "r-d-json", "azure", "sarif" ] From 019a9d697cb2b9de4b710d163fdd0b2a0019c79a Mon Sep 17 00:00:00 2001 From: Tobias Fischer Date: Sat, 1 Jun 2024 18:15:41 +0200 Subject: [PATCH 4/7] output(RDJson): avoid setting `suggestions: null` if there are no fixes --- crates/ruff_linter/src/message/rdjson.rs | 49 ++++++++----------- ...inter__message__rdjson__tests__output.snap | 3 +- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/crates/ruff_linter/src/message/rdjson.rs b/crates/ruff_linter/src/message/rdjson.rs index dfe6c31160691..a5abceb49bba9 100644 --- a/crates/ruff_linter/src/message/rdjson.rs +++ b/crates/ruff_linter/src/message/rdjson.rs @@ -60,15 +60,10 @@ impl Serialize for ExpandedMessages<'_> { pub(crate) fn message_to_rdjson_value(message: &Message) -> Value { let source_code = message.file.to_source_code(); - let fix = message.fix.as_ref().map(|fix| ExpandedEdits { - edits: fix.edits(), - source_code: &source_code, - }); - let start_location = source_code.source_location(message.start()); let end_location = source_code.source_location(message.end()); - json!({ + let mut result = json!({ "message": message.kind.body, "location": { "path": message.filename(), @@ -78,34 +73,32 @@ pub(crate) fn message_to_rdjson_value(message: &Message) -> Value { "value": message.kind.rule().noqa_code().to_string(), "url": message.kind.rule().url(), }, - "suggestions": fix, }) + .as_object() + .unwrap() + .clone(); + + if let Some(fix) = message.fix.as_ref() { + result.insert( + "suggestions".into(), + rdjson_suggestions(fix.edits(), &source_code), + ); + }; + + return Value::Object(result); } -struct ExpandedEdits<'a> { - edits: &'a [Edit], - source_code: &'a SourceCode<'a, 'a>, -} - -impl Serialize for ExpandedEdits<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut s = serializer.serialize_seq(Some(self.edits.len()))?; - - for edit in self.edits { - let location = self.source_code.source_location(edit.start()); - let end_location = self.source_code.source_location(edit.end()); +fn rdjson_suggestions(edits: &[Edit], source_code: &SourceCode) -> Value { + let mut suggestions: Vec = vec![]; - s.serialize_element(&json!({ - "range": rdjson_range(location, end_location), - "text": edit.content().unwrap_or_default(), - }))?; - } + for edit in edits { + let location = source_code.source_location(edit.start()); + let end_location = source_code.source_location(edit.end()); - s.end() + suggestions.push(json!({"range": rdjson_range(location, end_location), "text": edit.content().unwrap_or_default()})); } + + return Value::Array(suggestions); } fn rdjson_range(start: SourceLocation, end: SourceLocation) -> Value { diff --git a/crates/ruff_linter/src/message/snapshots/ruff_linter__message__rdjson__tests__output.snap b/crates/ruff_linter/src/message/snapshots/ruff_linter__message__rdjson__tests__output.snap index 13752f7b58365..cbb8d6c632796 100644 --- a/crates/ruff_linter/src/message/snapshots/ruff_linter__message__rdjson__tests__output.snap +++ b/crates/ruff_linter/src/message/snapshots/ruff_linter__message__rdjson__tests__output.snap @@ -92,8 +92,7 @@ expression: content } } }, - "message": "Undefined name `a`", - "suggestions": null + "message": "Undefined name `a`" } ], "severity": "warning", From acebcae81b98984e97724daf4ea24d1b55172f1d Mon Sep 17 00:00:00 2001 From: Tobias Fischer Date: Sat, 1 Jun 2024 18:35:44 +0200 Subject: [PATCH 5/7] output(RDJson): apply clippy suggestions --- crates/ruff_linter/src/message/rdjson.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/ruff_linter/src/message/rdjson.rs b/crates/ruff_linter/src/message/rdjson.rs index a5abceb49bba9..b90e0212dbae4 100644 --- a/crates/ruff_linter/src/message/rdjson.rs +++ b/crates/ruff_linter/src/message/rdjson.rs @@ -67,7 +67,7 @@ pub(crate) fn message_to_rdjson_value(message: &Message) -> Value { "message": message.kind.body, "location": { "path": message.filename(), - "range": rdjson_range(start_location, end_location), + "range": rdjson_range(&start_location, &end_location), }, "code": { "value": message.kind.rule().noqa_code().to_string(), @@ -85,7 +85,7 @@ pub(crate) fn message_to_rdjson_value(message: &Message) -> Value { ); }; - return Value::Object(result); + Value::Object(result) } fn rdjson_suggestions(edits: &[Edit], source_code: &SourceCode) -> Value { @@ -95,13 +95,13 @@ fn rdjson_suggestions(edits: &[Edit], source_code: &SourceCode) -> Value { let location = source_code.source_location(edit.start()); let end_location = source_code.source_location(edit.end()); - suggestions.push(json!({"range": rdjson_range(location, end_location), "text": edit.content().unwrap_or_default()})); + suggestions.push(json!({"range": rdjson_range(&location, &end_location), "text": edit.content().unwrap_or_default()})); } - return Value::Array(suggestions); + Value::Array(suggestions) } -fn rdjson_range(start: SourceLocation, end: SourceLocation) -> Value { +fn rdjson_range(start: &SourceLocation, end: &SourceLocation) -> Value { json!({ "start": { "line": start.row, From 1ea4203082da1793095a91c5dc74dabf8ed2ada3 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 2 Jun 2024 13:50:58 -0400 Subject: [PATCH 6/7] Rename to rdjson --- crates/ruff/src/printer.rs | 6 +++--- crates/ruff_linter/src/message/mod.rs | 2 +- crates/ruff_linter/src/message/rdjson.rs | 8 ++++---- crates/ruff_linter/src/settings/types.rs | 4 ++-- docs/configuration.md | 2 +- ruff.schema.json | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/ruff/src/printer.rs b/crates/ruff/src/printer.rs index 1d2a18569b786..3931a5a13df3a 100644 --- a/crates/ruff/src/printer.rs +++ b/crates/ruff/src/printer.rs @@ -13,7 +13,7 @@ use ruff_linter::fs::relativize_path; use ruff_linter::logging::LogLevel; use ruff_linter::message::{ AzureEmitter, Emitter, EmitterContext, GithubEmitter, GitlabEmitter, GroupedEmitter, - JsonEmitter, JsonLinesEmitter, JunitEmitter, PylintEmitter, RDJsonEmitter, SarifEmitter, + JsonEmitter, JsonLinesEmitter, JunitEmitter, PylintEmitter, RdjsonEmitter, SarifEmitter, TextEmitter, }; use ruff_linter::notify_user; @@ -243,8 +243,8 @@ impl Printer { SerializationFormat::Json => { JsonEmitter.emit(writer, &diagnostics.messages, &context)?; } - SerializationFormat::RDJson => { - RDJsonEmitter.emit(writer, &diagnostics.messages, &context)?; + SerializationFormat::Rdjson => { + RdjsonEmitter.emit(writer, &diagnostics.messages, &context)?; } SerializationFormat::JsonLines => { JsonLinesEmitter.emit(writer, &diagnostics.messages, &context)?; diff --git a/crates/ruff_linter/src/message/mod.rs b/crates/ruff_linter/src/message/mod.rs index 02cd08e1a43c4..7e95fc9d14cba 100644 --- a/crates/ruff_linter/src/message/mod.rs +++ b/crates/ruff_linter/src/message/mod.rs @@ -13,7 +13,7 @@ pub use json::JsonEmitter; pub use json_lines::JsonLinesEmitter; pub use junit::JunitEmitter; pub use pylint::PylintEmitter; -pub use rdjson::RDJsonEmitter; +pub use rdjson::RdjsonEmitter; use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix}; use ruff_notebook::NotebookIndex; use ruff_source_file::{SourceFile, SourceLocation}; diff --git a/crates/ruff_linter/src/message/rdjson.rs b/crates/ruff_linter/src/message/rdjson.rs index b90e0212dbae4..bf7bc3ec89b38 100644 --- a/crates/ruff_linter/src/message/rdjson.rs +++ b/crates/ruff_linter/src/message/rdjson.rs @@ -12,9 +12,9 @@ use crate::message::{Emitter, EmitterContext, Message, SourceLocation}; use crate::registry::AsRule; #[derive(Default)] -pub struct RDJsonEmitter; +pub struct RdjsonEmitter; -impl Emitter for RDJsonEmitter { +impl Emitter for RdjsonEmitter { fn emit( &mut self, writer: &mut dyn Write, @@ -119,11 +119,11 @@ mod tests { use insta::assert_snapshot; use crate::message::tests::{capture_emitter_output, create_messages}; - use crate::message::RDJsonEmitter; + use crate::message::RdjsonEmitter; #[test] fn output() { - let mut emitter = RDJsonEmitter; + let mut emitter = RdjsonEmitter; let content = capture_emitter_output(&mut emitter, &create_messages()); assert_snapshot!(content); diff --git a/crates/ruff_linter/src/settings/types.rs b/crates/ruff_linter/src/settings/types.rs index ae8770fe96ca1..aed0b28129cd4 100644 --- a/crates/ruff_linter/src/settings/types.rs +++ b/crates/ruff_linter/src/settings/types.rs @@ -513,7 +513,7 @@ pub enum SerializationFormat { Github, Gitlab, Pylint, - RDJson, + Rdjson, Azure, Sarif, } @@ -531,7 +531,7 @@ impl Display for SerializationFormat { Self::Github => write!(f, "github"), Self::Gitlab => write!(f, "gitlab"), Self::Pylint => write!(f, "pylint"), - Self::RDJson => write!(f, "rdjson"), + Self::Rdjson => write!(f, "rdjson"), Self::Azure => write!(f, "azure"), Self::Sarif => write!(f, "sarif"), } diff --git a/docs/configuration.md b/docs/configuration.md index b1993697db654..d675a36da1261 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -599,7 +599,7 @@ Options: format is "concise". In preview mode, the default serialization format is "full" [env: RUFF_OUTPUT_FORMAT=] [possible values: text, concise, full, json, json-lines, junit, grouped, github, gitlab, - pylint, rd-json, azure, sarif] + pylint, rdjson, azure, sarif] -o, --output-file Specify file to write the linter output to (default: stdout) [env: RUFF_OUTPUT_FILE=] diff --git a/ruff.schema.json b/ruff.schema.json index 4216548a5c56c..936eeb575d63b 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3947,7 +3947,7 @@ "github", "gitlab", "pylint", - "r-d-json", + "rdjson", "azure", "sarif" ] From 63abe85b0ce7cd2b6c45ae2461bf5366d218b7b8 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 2 Jun 2024 13:55:50 -0400 Subject: [PATCH 7/7] Remove unwrap --- crates/ruff_linter/src/message/rdjson.rs | 73 +++++++++++++----------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/crates/ruff_linter/src/message/rdjson.rs b/crates/ruff_linter/src/message/rdjson.rs index bf7bc3ec89b38..9d3ff50411f2a 100644 --- a/crates/ruff_linter/src/message/rdjson.rs +++ b/crates/ruff_linter/src/message/rdjson.rs @@ -57,48 +57,55 @@ impl Serialize for ExpandedMessages<'_> { } } -pub(crate) fn message_to_rdjson_value(message: &Message) -> Value { +fn message_to_rdjson_value(message: &Message) -> Value { let source_code = message.file.to_source_code(); let start_location = source_code.source_location(message.start()); let end_location = source_code.source_location(message.end()); - let mut result = json!({ - "message": message.kind.body, - "location": { - "path": message.filename(), - "range": rdjson_range(&start_location, &end_location), - }, - "code": { - "value": message.kind.rule().noqa_code().to_string(), - "url": message.kind.rule().url(), - }, - }) - .as_object() - .unwrap() - .clone(); - if let Some(fix) = message.fix.as_ref() { - result.insert( - "suggestions".into(), - rdjson_suggestions(fix.edits(), &source_code), - ); - }; - - Value::Object(result) + json!({ + "message": message.kind.body, + "location": { + "path": message.filename(), + "range": rdjson_range(&start_location, &end_location), + }, + "code": { + "value": message.kind.rule().noqa_code().to_string(), + "url": message.kind.rule().url(), + }, + "suggestions": rdjson_suggestions(fix.edits(), &source_code), + }) + } else { + json!({ + "message": message.kind.body, + "location": { + "path": message.filename(), + "range": rdjson_range(&start_location, &end_location), + }, + "code": { + "value": message.kind.rule().noqa_code().to_string(), + "url": message.kind.rule().url(), + }, + }) + } } fn rdjson_suggestions(edits: &[Edit], source_code: &SourceCode) -> Value { - let mut suggestions: Vec = vec![]; - - for edit in edits { - let location = source_code.source_location(edit.start()); - let end_location = source_code.source_location(edit.end()); - - suggestions.push(json!({"range": rdjson_range(&location, &end_location), "text": edit.content().unwrap_or_default()})); - } - - Value::Array(suggestions) + Value::Array( + edits + .iter() + .map(|edit| { + let location = source_code.source_location(edit.start()); + let end_location = source_code.source_location(edit.end()); + + json!({ + "range": rdjson_range(&location, &end_location), + "text": edit.content().unwrap_or_default(), + }) + }) + .collect(), + ) } fn rdjson_range(start: &SourceLocation, end: &SourceLocation) -> Value {