This is a utility to insert XML markups to the code fragments that causing diagnostic error or warning in Rust. Rust compiler produces many diagnostic information to the console, using file name and line numbers to indicate the exact location. However, that requires a programmer to go back and forth between the command console and an editor. This utility would make it easier to insert the diagnostic messages in place, similar to the idea of 'in-place' editing concept.
Moreover, the diagnostic information added into the code sequence could enable further transformer-based machine learning approaches to analyse the semantics of Rust programas.
Currently we integrate it with rust-clippy
, but it could also work for cargo-build
commands.
The format of the marked up code would look like the following:
```rust use cargometadata::Message; use std::{ collections::HashMap, fs::readto_string, path::PathBuf, process::{Command, Stdio}, };
struct Ran { name: String, start: usize, end: usize, note: String, }
// insert diagnostic code as an markup element around the code causing the diagnostic message
fn markup(source: &[u8], map: Vec
// Run cargo clippy to generate warnings from "foo.rs" into temporary "foo.rs.1" files fn main() { removepreviouslygeneratedfiles(); let mut command = Command::new("cargo") // .args(["build", "--message-format=json"]) .args(["clippy", "--message-format=json"]) .stdout(Stdio::piped()) .spawn() .unwrap(); let reader = std::io::BufReader::new(command.stdout.take().unwrap()); let mut map = HashMap::new(); for message in cargometadata::Message::parsestream(reader) { if let Message::CompilerMessage(msg) = message.unwrap() { <#[Warning(clippy::dbgmacro)>dbg!(&msg)#[Warning(clippy::dbg_macro)>[[
&msg]];
let nm = msg
.message
.children
.intoiter()
.map(|x| {
let ss = x
.spans
.iter()
.filter(|y| {
y.suggestedreplacement.issome()
&& !&y
.suggestedreplacement
.asref()
.unwrap()
.asstr()
.isempty()
})
.filter(|x| {
x.suggestedreplacement.issome()
&& !x.suggestedreplacement.asref().unwrap().isempty()
})
.map(|x| x.suggestedreplacement.asref().unwrap().asstr())
.collect::
]]; let y = <#[Warning(clippy::asconversions)>s.byteend as usize#[Warning(clippy::asconversions)>[[ ]]; let r = Ran { // name: msg.message.message.clone(), name: format!( "#[{:?}({})", msg.message.level, msg.message.code.clone().unwrap().code ), start: x, end: y, note: <#[Warning(clippy::uselessformat)>format!("{}", &nm)#[Warning(clippy::useless_format)>[[
(&nm).tostring()]], }; let filename = s.filename; <#[Warning(clippy::mapentry)>if !map.containskey(&filename) { let v = vec![r]; map.insert(filename, v); } else { let v = map.getmut(&filename).unwrap(); v.push(r); }#[Warning(clippy::mapentry)>[[
if let std::collections::hashmap::Entry::Vacant(e) = map.entry(filename) { let v = vec![r]; e.insert(v); } else { let v = map.getmut(&filename).unwrap(); v.push(r); }]] } } } for file in map.keys() { let source = readtostring(file).ok().unwrap(); let v = map.get(file).unwrap(); let output = markup(source.asbytes(), v.tovec()); let path = PathBuf::from(file); let filename = path.parent().unwrap().join(format!( "{}.rs.1", path.filestem().unwrap().tostringlossy() )); println!("Marked warning(s) into {:?}", &filename); if !filename.parent().unwrap().exists() { std::fs::createdir(filename.parent().unwrap()).ok(); } std::fs::write(&filename, std::str::fromutf8(&output).unwrap()).ok(); } let _output = command.wait().expect("Couldn't get cargo's exit status"); }
fn removepreviouslygeneratedfiles() { let command = Command::new("find") .args([".", "-name", "*.rs.1"]) .stdout(Stdio::piped()) .spawn() .unwrap(); let output = command .waitwithoutput() .expect("failed to aquire programm output") .stdout; if !output.isempty() { println!("Removed previously generated warning files") } String::fromutf8(output) .expect("programm output was not valid utf-8") .split('\n') .foreach(|tmp| { let mut command = Command::new("rm") .args(["-f", tmp]) .stdout(Stdio::piped()) .spawn() .unwrap(); command.wait().expect("problem with file deletion"); }); } ```
Note that this is a result applying the rust-diagnostic
utilility on its own implementation, i.e., eating our own dog food :-)
rust
<#[Warning(clippy::dbg_macro)>dbg!(&msg)</#[Warning(clippy::dbg_macro)>
contains a Warning
as the diagnostic code, and clippy::dbg_macro
as the name of the lint rule violated by the code dbg!(&msg)
. CDATA
so that it won't interfere with any XML parser. At the moment, I just print them out so it may interfere with XML parser and Rust parser :-( cargo-build
or rust-clippy
as the source of the diagnostic information. --message-format=json
option to get diagnostic information from the Rust compiler, which saves tremendous effort in modifying the Rust compiler. Now our solution is kind of independent from the Rust compiler implementations.