use codespan_reporting::diagnostic::{Diagnostic, Label, LabelStyle, Severity}; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use std::ops::Range; use std::path::PathBuf; /// A warning about a blocking call detected in an async context. #[derive(Debug, Clone)] pub struct Warning { /// Path to the source file. pub path: PathBuf, /// Byte offset range of the call expression within the source file. pub span: Range, /// The resolved call path that matched the blocklist. pub call_path: String, /// Category from the blocklist entry (e.g., "filesystem"). pub category: String, /// Help text from the blocklist entry. pub help: String, /// Description of the async context (e.g., "async fn `load_file`"). pub context: String, } /// Render all warnings for a single file using codespan-reporting. pub fn emit_file_warnings(warnings: &[Warning], source: &str, path: &str, format: &str) { if warnings.is_empty() { return; } match format { "json" => { for warning in warnings { emit_json(warning); } } _ => emit_human_batch(warnings, source, path), } } fn emit_human_batch(warnings: &[Warning], source: &str, path: &str) { let mut files = SimpleFiles::new(); let file_id = files.add(path, source); let writer = StandardStream::stderr(ColorChoice::Auto); let config = term::Config::default(); for warning in warnings { let diagnostic = Diagnostic::new(Severity::Warning) .with_code("blocking-in-async") .with_message("blocking call in async context") .with_labels(vec![Label::new( LabelStyle::Primary, file_id, warning.span.clone(), ) .with_message(format!( "`{}` is a blocking {} operation", warning.call_path, warning.category ))]) .with_notes(vec![ format!("help: {}", warning.help), format!("context: {}", warning.context), ]); term::emit_to_write_style(&mut writer.lock(), &config, &files, &diagnostic) .expect("failed to write diagnostic"); } } fn emit_json(warning: &Warning) { println!( r#"{{"level":"warning","code":"blocking-in-async","path":"{}","byte_start":{},"byte_end":{},"call":"{}","category":"{}","help":"{}","context":"{}"}}"#, warning.path.display(), warning.span.start, warning.span.end, warning.call_path, warning.category, warning.help, warning.context, ); }