Skip to content

Commit

Permalink
feat: Pavex deduplicates diagnostics, thus reducing visual noise when…
Browse files Browse the repository at this point in the history
… code generation fails
  • Loading branch information
LukeMathWalker committed Oct 13, 2024
1 parent a616e62 commit 93a0eb4
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 54 deletions.
85 changes: 54 additions & 31 deletions libs/pavexc_cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::{BTreeMap, HashMap};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf};
use std::process::ExitCode;
Expand Down Expand Up @@ -320,22 +320,25 @@ fn generate(
let file = fs_err::OpenOptions::new().read(true).open(blueprint)?;
ron::de::from_reader(&file)?
};
let mut reporter = DiagnosticReporter::new(color_profile);
// We use the path to the generated application crate as a fingerprint for the project.
let project_fingerprint = output.to_string_lossy().into_owned();
let app = match App::build(blueprint, project_fingerprint, docs_toolchain) {
Ok((a, warnings)) => {
for e in warnings {
let (app, issues) = match App::build(blueprint, project_fingerprint, docs_toolchain) {
Ok((a, issues)) => {
for e in &issues {
assert_eq!(e.severity(), Some(Severity::Warning));
print_report(&e, color_profile);
}
a
}
Err(issues) => {
for e in issues {
print_report(&e, color_profile);
}
return Ok(ExitCode::FAILURE);
(Some(a), issues)
}
Err(issues) => (None, issues),
};

for e in issues {
reporter.print_report(&e);
}

let Some(app) = app else {
return Ok(ExitCode::FAILURE);
};
if let Some(diagnostic_path) = diagnostics {
app.diagnostic_representation()
Expand All @@ -350,33 +353,53 @@ fn generate(
generated_app.persist(&output, &mut writer)?;
if let Err(errors) = writer.verify() {
for e in errors {
print_report(&e, color_profile);
reporter.print_report(&e);
}
return Ok(ExitCode::FAILURE);
}
Ok(ExitCode::SUCCESS)
}

fn print_report(e: &miette::Report, color_profile: Color) {
let use_color = use_color_on_stderr(color_profile);
match e.severity() {
None | Some(Severity::Error) => {
if use_color {
eprintln!("{}: {e:?}", "ERROR".bold().red());
} else {
eprintln!("ERROR: {e:?}");
}
}
Some(Severity::Warning) => {
if use_color {
eprintln!("{}: {e:?}", "WARNING".bold().yellow());
} else {
eprintln!("WARNING: {e:?}");
}
/// The compiler may emit the same diagnostic more than once
/// (for a variety of reasons). We use this helper to dedup them.
struct DiagnosticReporter {
already_emitted: HashSet<String>,
use_color: bool,
}

impl DiagnosticReporter {
fn new(color_profile: Color) -> Self {
let use_color = use_color_on_stderr(color_profile);
Self {
already_emitted: Default::default(),
use_color,
}
_ => {
unreachable!()
}
fn print_report(&mut self, e: &miette::Report) {
let formatted = format!("{e:?}");
if self.already_emitted.contains(&formatted) {
// Avoid printing the same diagnostic multiple times.
return;
}
let prefix = match e.severity() {
None | Some(Severity::Error) => {
let mut p = "ERROR".to_string();
if self.use_color {
p = p.bold().red().to_string();
}
p
}
Some(Severity::Warning) => {
let mut p = "WARNING".to_string();
if self.use_color {
p = p.bold().yellow().to_string();
}
p
}
_ => unreachable!(),
};
eprintln!("{prefix}: {formatted}");
self.already_emitted.insert(formatted);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,3 @@
ERROR:
× I can't generate code that will pass the borrow checker *and* match the
│ instructions in your blueprint:
│ - One of the components in the call graph for `app::wrap`
│ consumes `app::A` by value
│ - But, later on, the same type is used in the call graph of
│ `app::post`.
│ You forbid cloning of `app::A`, therefore I can't resolve this
│ conflict.
│
│ help: Allow me to clone `app::A` in order to satisfy the borrow
│ checker.
│ You can do so by invoking `.clone_if_necessary()` after having
│ registered your constructor.
│ ☞
│ ╭─[borrow_checker/across_middlewares/type_is_not_cloned_if_consumed_by_wrap_but_needed_by_post/src/lib.rs:30:1]
│ 30 │ let mut bp = Blueprint::new();
│ 31 │ bp.request_scoped(f!(crate::a)).never_clone();
│ ·  ──────┬─────
│ · ╰── The constructor was registered here
│ 32 │ bp.post_process(f!(crate::post));
│ ╰────

ERROR:
× I can't generate code that will pass the borrow checker *and* match the
│ instructions in your blueprint:
Expand Down

0 comments on commit 93a0eb4

Please sign in to comment.