Added hint to bail!, warning!, and error! (#2756)

This commit is contained in:
Sébastien d'Herbais de Thun 2023-11-27 11:17:47 +01:00 committed by GitHub
parent 219c1c9ed0
commit 2c85161a27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 149 additions and 90 deletions

View File

@ -18,69 +18,105 @@ use crate::{World, WorldExt};
/// `StrResult`. If called with a span, a string and format args, returns
/// a `SourceResult`.
///
/// You can also emit hints with the `; hint: "..."` syntax.
///
/// ```
/// bail!("bailing with a {}", "string result");
/// bail!(span, "bailing with a {}", "source result");
/// bail!(
/// span, "bailing with a {}", "source result";
/// hint: "hint 1"
/// );
/// bail!(
/// span, "bailing with a {}", "source result";
/// hint: "hint 1";
/// hint: "hint 2";
/// );
/// ```
#[macro_export]
#[doc(hidden)]
macro_rules! __bail {
// For bail!("just a {}", "string")
($fmt:literal $(, $arg:expr)* $(,)?) => {
return Err($crate::diag::eco_format!($fmt, $($arg),*))
return Err($crate::diag::error!(
$fmt, $($arg),*
))
};
// For bail!(error!(..))
($error:expr) => {
return Err(::ecow::eco_vec![$error])
};
($span:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {
return Err(::ecow::eco_vec![$crate::diag::SourceDiagnostic::error(
$span,
$crate::diag::eco_format!($fmt, $($arg),*),
)])
// For bail(span, ...)
($($tts:tt)*) => {
return Err(::ecow::eco_vec![$crate::diag::error!($($tts)*)])
};
}
#[doc(inline)]
pub use crate::__bail as bail;
#[doc(inline)]
pub use crate::__error as error;
#[doc(inline)]
pub use crate::__warning as warning;
#[doc(hidden)]
pub use ecow::eco_format;
#[doc(hidden)]
pub use ecow::EcoString;
/// Construct an [`EcoString`] or [`SourceDiagnostic`] with severity `Error`.
#[macro_export]
#[doc(hidden)]
macro_rules! __error {
// For bail!("just a {}", "string").
($fmt:literal $(, $arg:expr)* $(,)?) => {
$crate::diag::eco_format!($fmt, $($arg),*)
};
($span:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {
// For bail!(span, ...)
(
$span:expr, $fmt:literal $(, $arg:expr)*
$(; hint: $hint:literal $(, $hint_arg:expr)*)*
$(,)?
) => {
$crate::diag::SourceDiagnostic::error(
$span,
$crate::diag::eco_format!($fmt, $($arg),*),
)
) $(.with_hint($crate::diag::eco_format!($hint, $($hint_arg),*)))*
};
}
/// Construct a [`SourceDiagnostic`] with severity `Warning`.
///
/// You can also emit hints with the `; hint: "..."` syntax.
///
/// ```
/// warning!(span, "warning with a {}", "source result");
/// warning!(
/// span, "warning with a {}", "source result";
/// hint: "hint 1"
/// );
/// warning!(
/// span, "warning with a {}", "source result";
/// hint: "hint 1";
/// hint: "hint 2";
/// );
/// ```
#[macro_export]
#[doc(hidden)]
macro_rules! __warning {
($span:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {
(
$span:expr,
$fmt:literal $(, $arg:expr)*
$(; hint: $hint:literal $(, $hint_arg:expr)*)*
$(,)?
) => {
$crate::diag::SourceDiagnostic::warning(
$span,
$crate::diag::eco_format!($fmt, $($arg),*),
)
) $(.with_hint($crate::diag::eco_format!($hint, $($hint_arg),*)))*
};
}
#[rustfmt::skip]
#[doc(inline)]
pub use {
crate::__bail as bail,
crate::__error as error,
crate::__warning as warning,
ecow::{eco_format, EcoString},
};
/// A result that can carry multiple source errors.
pub type SourceResult<T> = Result<T, EcoVec<SourceDiagnostic>>;

View File

@ -139,11 +139,12 @@ impl Eval for ast::Strong<'_> {
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
let body = self.body();
if body.exprs().next().is_none() {
vm.engine.tracer.warn(
warning!(self.span(), "no text within stars").with_hint(
"using multiple consecutive stars (e.g. **) has no additional effect",
),
);
vm.engine
.tracer
.warn(warning!(
self.span(), "no text within stars";
hint: "using multiple consecutive stars (e.g. **) has no additional effect",
));
}
Ok(StrongElem::new(body.eval(vm)?).pack())
@ -159,9 +160,10 @@ impl Eval for ast::Emph<'_> {
if body.exprs().next().is_none() {
vm.engine
.tracer
.warn(warning!(self.span(), "no text within underscores").with_hint(
"using multiple consecutive underscores (e.g. __) has no additional effect"
));
.warn(warning!(
self.span(), "no text within underscores";
hint: "using multiple consecutive underscores (e.g. __) has no additional effect"
));
}
Ok(EmphElem::new(body.eval(vm)?).pack())

View File

@ -5,7 +5,7 @@ use std::iter::repeat;
use ecow::{eco_format, EcoString, EcoVec};
use crate::diag::{bail, error, StrResult};
use crate::diag::{bail, StrResult};
use crate::foundations::{cast, func, repr, scope, ty, Repr};
/// A version with an arbitrary number of components.
@ -43,7 +43,7 @@ impl Version {
.iter()
.zip(Self::COMPONENTS)
.find_map(|(&i, s)| (s == name).then_some(i as i64))
.ok_or_else(|| error!("unknown version component"))
.ok_or_else(|| "unknown version component".into())
}
/// Push a component to the end of this version.

View File

@ -71,7 +71,7 @@ pub(crate) use self::inline::*;
use comemo::{Tracked, TrackedMut};
use crate::diag::{bail, error, SourceResult};
use crate::diag::{bail, SourceResult};
use crate::engine::{Engine, Route};
use crate::eval::Tracer;
use crate::foundations::{category, Category, Content, Scope, StyleChain};
@ -238,8 +238,10 @@ impl Layout for Content {
};
if engine.route.exceeding() {
bail!(error!(content.span(), "maximum layout depth exceeded")
.with_hint("try to reduce the amount of nesting in your layout"));
bail!(
content.span(), "maximum layout depth exceeded";
hint: "try to reduce the amount of nesting in your layout",
);
}
let scratch = Scratch::default();

View File

@ -143,10 +143,10 @@ fn typeset(
}
if iter >= 5 {
tracer.warn(
warning!(Span::detached(), "layout did not converge within 5 attempts",)
.with_hint("check if any states or queries are updating themselves"),
);
tracer.warn(warning!(
Span::detached(), "layout did not converge within 5 attempts";
hint: "check if any states or queries are updating themselves"
));
break;
}
}

View File

@ -3,7 +3,7 @@ use std::str::FromStr;
use comemo::Prehashed;
use crate::diag::{bail, error, At, SourceResult, StrResult};
use crate::diag::{bail, At, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Content, Finalize, Label, NativeElement, Show, Smart, StyleChain,
@ -280,9 +280,10 @@ impl Show for FootnoteEntry {
let numbering = note.numbering(default);
let counter = Counter::of(FootnoteElem::elem());
let Some(loc) = note.location() else {
bail!(error!(self.span(), "footnote entry must have a location").with_hint(
"try using a query or a show rule to customize the footnote instead"
))
bail!(
self.span(), "footnote entry must have a location";
hint: "try using a query or a show rule to customize the footnote instead"
);
};
let num = counter.at(engine, loc)?.display(engine, numbering)?;

View File

@ -1,7 +1,7 @@
use std::num::NonZeroUsize;
use std::str::FromStr;
use crate::diag::{bail, error, At, SourceResult};
use crate::diag::{bail, At, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, select_where, Content, Finalize, Func, LocatableSelector,
@ -492,10 +492,10 @@ impl Show for OutlineEntry {
// In case a user constructs an outline entry with an arbitrary element.
let Some(location) = elem.location() else {
if elem.can::<dyn Locatable>() && elem.can::<dyn Outlinable>() {
bail!(error!(self.span(), "{} must have a location", elem.func().name())
.with_hint(
"try using a query or a show rule to customize the outline.entry instead",
))
bail!(
self.span(), "{} must have a location", elem.func().name();
hint: "try using a query or a show rule to customize the outline.entry instead",
)
} else {
bail!(self.span(), "cannot outline {}", elem.func().name())
}

View File

@ -10,7 +10,7 @@ use std::mem;
use smallvec::smallvec;
use typed_arena::Arena;
use crate::diag::{bail, error, SourceResult};
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
Content, Finalize, Guard, NativeElement, Recipe, Selector, Show, StyleChain,
@ -308,11 +308,11 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
if let Some(realized) = realize(self.engine, content, styles)? {
self.engine.route.increase();
if self.engine.route.exceeding() {
bail!(error!(content.span(), "maximum show rule depth exceeded")
.with_hint("check whether the show rule matches its own output")
.with_hint(
"this is a current compiler limitation that will be resolved in the future"
));
bail!(
content.span(), "maximum show rule depth exceeded";
hint: "check whether the show rule matches its own output";
hint: "this is a current compiler limitation that will be resolved in the future",
);
}
let stored = self.scratch.content.alloc(realized);
let v = self.accept(stored, styles);

View File

@ -34,7 +34,7 @@ use ecow::{eco_format, EcoString};
use rustybuzz::{Feature, Tag};
use ttf_parser::Rect;
use crate::diag::{bail, error, SourceResult, StrResult};
use crate::diag::{bail, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
cast, category, elem, Args, Array, Cast, Category, Construct, Content, Dict, Fold,
@ -228,11 +228,9 @@ pub struct TextElem {
if let Some(paint) = &paint {
if paint.v.relative() == Smart::Custom(RelativeTo::Self_) {
bail!(
error!(
paint.span,
"gradients and patterns on text must be relative to the parent"
)
.with_hint("make sure to set `relative: auto` on your text fill")
paint.span,
"gradients and patterns on text must be relative to the parent";
hint: "make sure to set `relative: auto` on your text fill"
);
}
}

View File

@ -10,7 +10,7 @@ use palette::{
Darken, Desaturate, FromColor, Lighten, Okhsva, OklabHue, RgbHue, Saturate, ShiftHue,
};
use crate::diag::{bail, error, At, SourceResult, StrResult};
use crate::diag::{bail, At, SourceResult, StrResult};
use crate::foundations::{
array, cast, func, repr, scope, ty, Args, Array, IntoValue, Module, Repr, Scope, Str,
Value,
@ -922,8 +922,10 @@ impl Color {
) -> SourceResult<Color> {
Ok(match self {
Self::Luma(_) => {
bail!(error!(span, "cannot saturate grayscale color")
.with_hint("try converting your color to RGB first"));
bail!(
span, "cannot saturate grayscale color";
hint: "try converting your color to RGB first"
);
}
Self::Oklab(_) => self.to_hsv().saturate(span, factor)?.to_oklab(),
Self::Oklch(_) => self.to_hsv().saturate(span, factor)?.to_oklch(),
@ -946,8 +948,10 @@ impl Color {
) -> SourceResult<Color> {
Ok(match self {
Self::Luma(_) => {
bail!(error!(span, "cannot desaturate grayscale color")
.with_hint("try converting your color to RGB first"));
bail!(
span, "cannot desaturate grayscale color";
hint: "try converting your color to RGB first"
);
}
Self::Oklab(_) => self.to_hsv().desaturate(span, factor)?.to_oklab(),
Self::Oklch(_) => self.to_hsv().desaturate(span, factor)?.to_oklch(),

View File

@ -6,7 +6,7 @@ use std::sync::Arc;
use ecow::EcoString;
use kurbo::Vec2;
use crate::diag::{bail, error, SourceResult};
use crate::diag::{bail, SourceResult};
use crate::foundations::{
array, cast, func, scope, ty, Args, Array, Cast, Func, IntoValue, Repr, Smart,
};
@ -231,8 +231,10 @@ impl Gradient {
};
if stops.len() < 2 {
bail!(error!(span, "a gradient must have at least two stops")
.with_hint("try filling the shape with a single color instead"));
bail!(
span, "a gradient must have at least two stops";
hint: "try filling the shape with a single color instead"
);
}
Ok(Self::Linear(Arc::new(LinearGradient {
@ -332,24 +334,29 @@ impl Gradient {
focal_radius: Spanned<Ratio>,
) -> SourceResult<Gradient> {
if stops.len() < 2 {
bail!(error!(span, "a gradient must have at least two stops")
.with_hint("try filling the shape with a single color instead"));
bail!(
span, "a gradient must have at least two stops";
hint: "try filling the shape with a single color instead"
);
}
if focal_radius.v > radius.v {
bail!(error!(
bail!(
focal_radius.span,
"the focal radius must be smaller than the end radius"
)
.with_hint("try using a focal radius of `0%` instead"));
"the focal radius must be smaller than the end radius";
hint: "try using a focal radius of `0%` instead"
);
}
let focal_center = focal_center.unwrap_or(center);
let d_center_sqr = (focal_center.x - center.x).get().powi(2)
+ (focal_center.y - center.y).get().powi(2);
if d_center_sqr.sqrt() >= (radius.v - focal_radius.v).get() {
bail!(error!(span, "the focal circle must be inside of the end circle")
.with_hint("try using a focal center of `auto` instead"));
bail!(
span,
"the focal circle must be inside of the end circle";
hint: "try using a focal center of `auto` instead"
);
}
Ok(Gradient::Radial(Arc::new(RadialGradient {
@ -419,8 +426,10 @@ impl Gradient {
center: Axes<Ratio>,
) -> SourceResult<Gradient> {
if stops.len() < 2 {
bail!(error!(span, "a gradient must have at least two stops")
.with_hint("try filling the shape with a single color instead"));
bail!(
span, "a gradient must have at least two stops";
hint: "try filling the shape with a single color instead"
);
}
Ok(Gradient::Conic(Arc::new(ConicGradient {
@ -1154,11 +1163,10 @@ fn process_stops(stops: &[Spanned<GradientStop>]) -> SourceResult<Vec<(Color, Ra
let mut last_stop = f64::NEG_INFINITY;
for Spanned { v: stop, span } in stops.iter() {
let Some(stop) = stop.offset else {
bail!(error!(
*span,
"either all stops must have an offset or none of them can"
)
.with_hint("try adding an offset to all stops"));
bail!(
*span, "either all stops must have an offset or none of them can";
hint: "try adding an offset to all stops"
);
};
if stop.get() < last_stop {
@ -1179,13 +1187,19 @@ fn process_stops(stops: &[Spanned<GradientStop>]) -> SourceResult<Vec<(Color, Ra
.collect::<SourceResult<Vec<_>>>()?;
if out[0].1 != Ratio::zero() {
bail!(error!(stops[0].span, "first stop must have an offset of 0%")
.with_hint("try setting this stop to `0%`"));
bail!(
stops[0].span,
"first stop must have an offset of 0";
hint: "try setting this stop to `0%`"
);
}
if out[out.len() - 1].1 != Ratio::one() {
bail!(error!(stops[0].span, "last stop must have an offset of 100%")
.with_hint("try setting this stop to `100%`"));
bail!(
stops[out.len() - 1].span,
"last stop must have an offset of 100%";
hint: "try setting this stop to `100%`"
);
}
return Ok(out);

View File

@ -4,7 +4,7 @@ use std::sync::Arc;
use comemo::Prehashed;
use ecow::{eco_format, EcoString};
use crate::diag::{bail, error, SourceResult};
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{func, scope, ty, Content, Repr, Smart, StyleChain};
use crate::layout::{Abs, Axes, Em, Frame, Layout, Length, Regions, Size};
@ -181,8 +181,10 @@ impl Pattern {
// Check that the frame is non-zero.
if size.is_auto() && frame.size().is_zero() {
bail!(error!(span, "pattern tile size must be non-zero")
.with_hint("try setting the size manually"));
bail!(
span, "pattern tile size must be non-zero";
hint: "try setting the size manually"
);
}
// Set the size of the frame if the size is enforced.