Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6aef01e
seed commit for fatality based errors
drahnr Dec 2, 2021
86cb205
fatality
drahnr Dec 2, 2021
61605f7
first draft of fatality
drahnr Dec 2, 2021
7789e08
cleanup
drahnr Dec 3, 2021
df37cbf
differnt approach
drahnr Dec 3, 2021
8254614
simplify
drahnr Dec 3, 2021
ec8564e
first working version for enums, with documentation
drahnr Dec 3, 2021
a59a2c4
add split
drahnr Jan 21, 2022
bef8549
fix simple split test case
drahnr Jan 21, 2022
226bbb4
extend README.md
drahnr Jan 21, 2022
fb14c73
update fatality impl
drahnr Jan 25, 2022
9a5e3ca
make tests passed
drahnr Jan 25, 2022
3e4dce0
apply fatality to first subsystem
drahnr Jan 26, 2022
ef4a734
fatality fixes
drahnr Jan 28, 2022
50b56ea
use fatality in a subsystem
drahnr Jan 30, 2022
664a1c9
fix subsystemg
drahnr Jan 30, 2022
0db9f15
fixup proc macro
drahnr Jan 30, 2022
a67484e
fix/test: log::*! do not execute when log handler is missing
drahnr Feb 1, 2022
eb6a205
fix spelling
drahnr Feb 1, 2022
d770480
rename Runtime2 to something sane
drahnr Feb 1, 2022
47b2335
allow nested split with `forward` annotations
drahnr Feb 4, 2022
e24e302
add free license
drahnr Feb 4, 2022
3fc6cd4
enable and fixup all tests
drahnr Feb 4, 2022
346fd5b
use external fatality
drahnr Feb 23, 2022
859922a
bump fatality dep
drahnr Feb 23, 2022
6b29441
migrate availability distribution
drahnr Feb 23, 2022
5b00044
more fatality usage
drahnr Feb 23, 2022
ffb66e3
chore: bump fatality to 0.0.6
drahnr Feb 23, 2022
df26cbf
fixup remaining subsystems
drahnr Feb 23, 2022
9466d1c
chore: fmt
drahnr Feb 23, 2022
6ca4079
make cargo spellcheck happy
drahnr Feb 23, 2022
721e1ec
remove single instance of `#[fatal(false)]`
drahnr Feb 24, 2022
3c3adba
last quality sweep
drahnr Feb 25, 2022
c5d7d10
fixup
drahnr Feb 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fatality fixes
  • Loading branch information
drahnr committed Feb 25, 2022
commit ef4a734280cf2a1a970576c3d18aa7f9c0b0d0e6
181 changes: 100 additions & 81 deletions node/fatality/proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,13 @@ mod kw {

#[derive(Clone, Debug)]
enum ResolutionMode {
/// Not relevant for fatality determination, always non-fatal.
None,
/// Fatal by default.
Fatal,
/// Specified via a `bool` argument `#[fatal(true)]` or `#[fatal(false)]`.
WithExplicitBool(LitBool),
/// Specified via a keyword argument `#[fatal(forward)]`.
Forward(kw::forward, Option<Ident>),
}

Expand Down Expand Up @@ -94,7 +98,7 @@ fn abs_helper_path(what: impl Into<Path>, loco: Span) -> Path {
let found_crate = if cfg!(test) {
FoundCrate::Itself
} else {
crate_name("fatality").expect("`fatality` is present in `Cargo.toml`")
crate_name("fatality").expect("`fatality` must be present in `Cargo.toml` for use. q.e.d")
};
let ts = match found_crate {
FoundCrate::Itself => quote!( crate::#what ),
Expand Down Expand Up @@ -229,10 +233,9 @@ fn variant_to_pattern(
)
},
Fields::Unnamed(ref fields) => {
let fwd_idx = if is_transparent {
// take the first one, TODO error if there is more than one
0_usize
} else {
let mut field_pats = if let ResolutionMode::Forward(..) =
dbg!(&requested_resolution_mode)
{
fields
.unnamed
.iter()
Expand All @@ -242,32 +245,57 @@ fn variant_to_pattern(
})
.ok_or_else(|| {
syn::Error::new(
span,
fields.span(),
"Must have a `#[source]` annotated field for `forward` with `fatality`",
)
})?
};
})?;

// obtain the i of the i-th unnamed field.
let fwd_idx = if is_transparent {
// take the first one, TODO error if there is more than one
0_usize
} else {
fields
.unnamed
.iter()
.enumerate()
.find_map(|(idx, field)| {
field
.attrs
.iter()
.find(|attr| attr.path.is_ident(&source))
.map(|_attr| idx)
})
.ok_or_else(|| {
syn::Error::new(
span,
"Must have a `#[source]` annotated field for `forward` with `fatality`",
)
})?
};

// create a pattern like this: `_, _, _, inner, ..`
let mut field_pats = std::iter::repeat(Pat::Wild(PatWild {
attrs: vec![],
underscore_token: Token![_](span),
}))
.take(fwd_idx)
.collect::<Vec<_>>();

let maybe_member_ref = if let ResolutionMode::Forward(..) = requested_resolution_mode {
Some(Pat::Ident(PatIdent {
field_pats.push(Pat::Ident(PatIdent {
attrs: vec![],
by_ref: Some(Token![ref](span)),
mutability: None,
ident: pat_capture_ident.clone(),
subpat: None,
}))
}));

field_pats
} else {
None
vec![]
};

let field_pats = std::iter::repeat(Pat::Wild(PatWild {
attrs: vec![],
underscore_token: Token![_](span),
}))
.take(fwd_idx)
.chain(maybe_member_ref)
.chain([Pat::Rest(PatRest { attrs: vec![], dot2_token: Token![..](span) })]);

field_pats.push(Pat::Rest(PatRest { attrs: vec![], dot2_token: Token![..](span) }));
dbg!(&field_pats);
(
Pat::TupleStruct(PatTupleStruct {
attrs: vec![],
Expand Down Expand Up @@ -308,33 +336,36 @@ impl ToTokens for VariantPattern {
fn to_tokens(&self, ts: &mut TokenStream) {
let variant_name = &self.0.ident;
let variant_fields = &self.0.fields;
let span = variant_fields.span();
let path = Path {
leading_colon: None,
segments: Punctuated::<PathSegment, Colon2>::from_iter(vec![PathSegment::from(
variant_name.clone(),
)]),
};

let pattern = match variant_fields {
Fields::Unit => Some(Pat::Path(PatPath { attrs: vec![], qself: None, path }))
.into_iter()
.collect::<Punctuated<Pat, Token![,]>>(),
Fields::Unnamed(unnamed) => unnamed
.unnamed
.iter()
.enumerate()
.map(|(ith, _field)| {
Pat::Ident(PatIdent {
attrs: vec![],
by_ref: None,
mutability: None,
ident: unnamed_fields_variant_pattern_constructor_binding_name(ith),
subpat: None,
match variant_fields {
Fields::Unit => {
ts.extend(quote! { #variant_name });
},
Fields::Unnamed(unnamed) => {
let pattern = unnamed
.unnamed
.iter()
.enumerate()
.map(|(ith, _field)| {
Pat::Ident(PatIdent {
attrs: vec![],
by_ref: None,
mutability: None,
ident: unnamed_fields_variant_pattern_constructor_binding_name(ith),
subpat: None,
})
})
})
.collect::<Punctuated<Pat, Token![,]>>(),
Fields::Named(named) => named
.collect::<Punctuated<Pat, Token![,]>>();
ts.extend(quote! { #variant_name(#pattern) });
},
Fields::Named(named) => {
let pattern = named
.named
.iter()
.map(|field| {
Expand All @@ -345,10 +376,10 @@ impl ToTokens for VariantPattern {
ident: field.ident.clone().expect("Named field has a name. qed"),
subpat: None,
})
})
.collect::<Punctuated<Pat, Token![,]>>(),
.collect::<Punctuated<Pat, Token![,]>>();
ts.extend(quote! { #variant_name{ #pattern } });
},
};
ts.extend(pattern.into_token_stream());
}
}

Expand All @@ -360,44 +391,32 @@ impl ToTokens for VariantConstructor {
fn to_tokens(&self, ts: &mut TokenStream) {
let variant_name = &self.0.ident;
let variant_fields = &self.0.fields;
let args = match variant_fields {
Fields::Named(named) => named
ts.extend(match variant_fields {
Fields::Unit => quote! { #variant_name },
Fields::Unnamed(unnamed) => {
let constructor = unnamed
.unnamed
.iter()
.enumerate()
.map(|(ith, _field)| {
unnamed_fields_variant_pattern_constructor_binding_name(ith)
})
.collect::<Punctuated<Ident, Token![,]>>();
quote! { #variant_name (#constructor) }
},
Fields::Named(named) => {
let constructor = named
.named
.iter()
.map(|field| field.ident.clone().expect("Named must have named fields. qed"))
.collect::<Punctuated<Ident, Token![,]>>()
.into_token_stream(),
Fields::Unit => TokenStream::new(),
Fields::Unnamed(unnamed) => unnamed
.unnamed
.iter()
.enumerate()
.map(|(ith, _field)| unnamed_fields_variant_pattern_constructor_binding_name(ith))
.collect::<Punctuated<Ident, Token![,]>>()
.into_token_stream(),
};
ts.extend(quote! {
#variant_name #args
});
.collect::<Punctuated<Ident, Token![,]>>();
quote!{ #variant_name { #constructor } }
}
}
);
}
}

fn variant_to_pattern_and_constructor(
variants: impl AsRef<[Variant]>,
) -> Result<IndexMap<Pat, VariantConstructor>, syn::Error> {
let variants = variants.as_ref();

Ok(variants
.iter()
.map(|variant| {
variant_to_pattern(
variant,
ResolutionMode::None, // dummy
)
.map(|(pat, _)| (pat, VariantConstructor(variant.clone())))
})
.collect::<Result<IndexMap<Pat, VariantConstructor>, syn::Error>>()?)
}

// Generate the Jfyi and Fatal sub enums.
fn trait_split_impl(
Expand Down Expand Up @@ -482,9 +501,9 @@ fn trait_split_impl(
fn split(self) -> ::std::result::Result<Self::Jfyi, Self::Fatal> {
match self {
// Fatal
#( Self:: #fatal_patterns => Err(#fatal_ident :: #fatal_constructors), )*
#( Self :: #fatal_patterns => Err(#fatal_ident :: #fatal_constructors), )*
// JFYI
#( Self:: #jfyi_patterns => Ok(#jfyi_ident :: #jfyi_constructors), )*
#( Self :: #jfyi_patterns => Ok(#jfyi_ident :: #jfyi_constructors), )*
}
}
}
Expand Down Expand Up @@ -546,10 +565,10 @@ fn fatality_gen(attr: Attr, item: ItemEnum) -> Result<TokenStream, syn::Error> {
.variants
.iter()
.map(move |variant| {
variant_to_pattern(
variant,
has_fatal_annotation.get(&*variant).cloned().unwrap_or_default(),
)
dbg!(variant_to_pattern(
dbg!(variant),
dbg!(has_fatal_annotation.get(&*variant).cloned()).unwrap_or_default(),
))
.and_then(|x| {
// currently we don't support, `forward` in combination with `splitable`
if let Attr::Splitable(keyword_splitable) = attr {
Expand Down
95 changes: 95 additions & 0 deletions node/fatality/proc-macro/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,37 @@ mod component {

mod basic {
use super::*;

#[test]
fn visibility_pub_crate_is_retained() {
run_test(
TokenStream::new(),
quote! {
pub(crate) enum Q {
#[fatal]
#[error(transparent)]
V(I),
}
},
quote! {
#[derive(crate::thiserror::Error, Debug)]
pub(crate) enum Q {
#[error(transparent)]
V(I),
}


impl crate::Fatality for Q {
fn is_fatal(&self) -> bool {
match self {
Self::V(..) => true,
}
}
}
},
);
}

#[test]
fn transparent_fatal_implicit() {
run_test(
Expand Down Expand Up @@ -186,6 +217,7 @@ mod basic {
enum Kaboom {
#[fatal(forward)]
#[error(transparent)]
// only one arg, that's ok, the first will be used
A(X),

#[fatal(forward)]
Expand Down Expand Up @@ -318,4 +350,67 @@ mod splitable {
},
);
}

#[test]
fn regression() {
run_test(
quote! {
splitable
},
quote! {
pub enum X {
#[fatal]
#[error("Cancelled")]
Inner(Foo),
}
},
quote! {
#[derive(crate::thiserror::Error, Debug)]
pub enum X {
#[error("Cancelled")]
Inner(Foo),
}

impl crate :: Fatality for X {
fn is_fatal (& self) -> bool {
match self {
Self :: Inner (..) => true ,
}
}
}

impl :: std :: convert :: From < FatalX > for X {
fn from (fatal : FatalX) -> Self {
match fatal {
FatalX :: Inner(arg_0) => Self :: Inner(arg_0),
}
}
}

impl :: std :: convert :: From < JfyiX > for X {
fn from (jfyi : JfyiX) -> Self {
match jfyi {

}
}
}

# [derive (crate :: thiserror :: Error , Debug)]
pub enum FatalX {
#[error("Cancelled")]
Inner (Foo) }
#[derive (crate :: thiserror :: Error , Debug)]
pub enum JfyiX { }
impl crate :: Split for X {
type Fatal = FatalX ;
type Jfyi = JfyiX ;
fn split (self) -> :: std :: result :: Result < Self :: Jfyi , Self :: Fatal > {
match self {
Self::Inner(arg_0) => Err (FatalX :: Inner(arg_0)) ,
}
}
}
},
);
}
}
Loading