Skip to content

Commit

Permalink
Add [snafu(implicit)]
Browse files Browse the repository at this point in the history
  • Loading branch information
shepmaster committed Sep 25, 2021
1 parent 7ece503 commit 4b5ebfc
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 35 deletions.
3 changes: 3 additions & 0 deletions compatibility-tests/compile-fail/tests/ui/attribute-misuse.rs
Expand Up @@ -7,6 +7,7 @@ mod enum_misuse {
#[snafu(source(true))]
#[snafu(backtrace)]
#[snafu(context)]
#[snafu(implicit)]
enum EnumError {
AVariant,
}
Expand All @@ -22,6 +23,7 @@ mod variant_misuse {
#[snafu(source)]
#[snafu(backtrace)]
#[snafu(crate_root(XXXX))]
#[snafu(implicit)]
AVariant,
}
}
Expand Down Expand Up @@ -56,6 +58,7 @@ mod struct_misuse {
#[snafu(source(true))]
#[snafu(backtrace)]
#[snafu(context)]
#[snafu(implicit)]
struct StructError(Box<UsableError>);
}

Expand Down
74 changes: 46 additions & 28 deletions compatibility-tests/compile-fail/tests/ui/attribute-misuse.stderr
Expand Up @@ -28,86 +28,104 @@ error: `context` attribute is only valid on enum variants or structs with named
9 | #[snafu(context)]
| ^^^^^^^

error: `implicit` attribute is only valid on enum variant or struct fields with a name, not on an enum
--> $DIR/attribute-misuse.rs:10:13
|
10 | #[snafu(implicit)]
| ^^^^^^^^

error: `source` attribute is only valid on enum variant or struct fields with a name, not on an enum variant
--> $DIR/attribute-misuse.rs:21:46
--> $DIR/attribute-misuse.rs:22:46
|
21 | #[snafu(display("an error variant"), source(from(XXXX, Box::new)))]
22 | #[snafu(display("an error variant"), source(from(XXXX, Box::new)))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: `source` attribute is only valid on enum variant or struct fields with a name, not on an enum variant
--> $DIR/attribute-misuse.rs:22:17
--> $DIR/attribute-misuse.rs:23:17
|
22 | #[snafu(source)]
23 | #[snafu(source)]
| ^^^^^^

error: `backtrace` attribute is only valid on enum variant or struct fields with a name, not on an enum variant
--> $DIR/attribute-misuse.rs:23:17
--> $DIR/attribute-misuse.rs:24:17
|
23 | #[snafu(backtrace)]
24 | #[snafu(backtrace)]
| ^^^^^^^^^

error: `crate_root` attribute is only valid on an enum or a struct, not on an enum variant
--> $DIR/attribute-misuse.rs:24:17
--> $DIR/attribute-misuse.rs:25:17
|
24 | #[snafu(crate_root(XXXX))]
25 | #[snafu(crate_root(XXXX))]
| ^^^^^^^^^^^^^^^^

error: `implicit` attribute is only valid on enum variant or struct fields with a name, not on an enum variant
--> $DIR/attribute-misuse.rs:26:17
|
26 | #[snafu(implicit)]
| ^^^^^^^^

error: `display` attribute is only valid on enum variants or structs with named fields, not on a field
--> $DIR/attribute-misuse.rs:35:21
--> $DIR/attribute-misuse.rs:37:21
|
35 | #[snafu(display("display should not work here"))]
37 | #[snafu(display("display should not work here"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: `visibility` attribute is only valid on an enum, enum variants, or a struct with named fields, not on a field
--> $DIR/attribute-misuse.rs:36:21
--> $DIR/attribute-misuse.rs:38:21
|
36 | #[snafu(visibility(pub))]
38 | #[snafu(visibility(pub))]
| ^^^^^^^^^^^^^^^

error: Incompatible attributes [`source(false)`, `source(from)`] specified on a field
--> $DIR/attribute-misuse.rs:38:21
--> $DIR/attribute-misuse.rs:40:21
|
38 | #[snafu(source(from(XXXX, Box::new)))]
40 | #[snafu(source(from(XXXX, Box::new)))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: `context` attribute is only valid on enum variants or structs with named fields, not on a field
--> $DIR/attribute-misuse.rs:39:21
--> $DIR/attribute-misuse.rs:41:21
|
39 | #[snafu(context)]
41 | #[snafu(context)]
| ^^^^^^^

error: `crate_root` attribute is only valid on an enum or a struct, not on a field
--> $DIR/attribute-misuse.rs:40:21
--> $DIR/attribute-misuse.rs:42:21
|
40 | #[snafu(crate_root(XXXX))]
42 | #[snafu(crate_root(XXXX))]
| ^^^^^^^^^^^^^^^^

error: `display` attribute is only valid on enum variants or structs with named fields, not on a tuple struct
--> $DIR/attribute-misuse.rs:53:13
--> $DIR/attribute-misuse.rs:55:13
|
53 | #[snafu(display("display should not work here"))]
55 | #[snafu(display("display should not work here"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: `visibility` attribute is only valid on an enum, enum variants, or a struct with named fields, not on a tuple struct
--> $DIR/attribute-misuse.rs:55:13
--> $DIR/attribute-misuse.rs:57:13
|
55 | #[snafu(visibility(pub))]
57 | #[snafu(visibility(pub))]
| ^^^^^^^^^^^^^^^

error: `source(bool)` attribute is only valid on enum variant or struct fields with a name, not on a tuple struct
--> $DIR/attribute-misuse.rs:56:13
--> $DIR/attribute-misuse.rs:58:13
|
56 | #[snafu(source(true))]
58 | #[snafu(source(true))]
| ^^^^^^^^^^^^

error: `backtrace` attribute is only valid on enum variant or struct fields with a name, not on a tuple struct
--> $DIR/attribute-misuse.rs:57:13
--> $DIR/attribute-misuse.rs:59:13
|
57 | #[snafu(backtrace)]
59 | #[snafu(backtrace)]
| ^^^^^^^^^

error: `context` attribute is only valid on enum variants or structs with named fields, not on a tuple struct
--> $DIR/attribute-misuse.rs:58:13
--> $DIR/attribute-misuse.rs:60:13
|
58 | #[snafu(context)]
60 | #[snafu(context)]
| ^^^^^^^

error: `implicit` attribute is only valid on enum variant or struct fields with a name, not on a tuple struct
--> $DIR/attribute-misuse.rs:61:13
|
61 | #[snafu(implicit)]
| ^^^^^^^^
@@ -0,0 +1,22 @@
use snafu::prelude::*;

#[derive(Debug)]
struct ImplicitData;

impl snafu::GenerateImplicitData for ImplicitData {
fn generate() -> Self {
Self
}
}

#[derive(Debug, Snafu)]
enum EnumError {
AVariant {
// Second attribute should be marked as duplicate
#[snafu(implicit)]
#[snafu(implicit)]
my_data: ImplicitData,
},
}

fn main() {}
@@ -0,0 +1,5 @@
error: Multiple `implicit` attributes are not supported on a field
--> $DIR/duplication-implicit-attributes.rs:17:17
|
17 | #[snafu(implicit)]
| ^^^^^^^^
@@ -1,4 +1,4 @@
error: expected one of: `backtrace`, `context`, `crate_root`, `display`, `source`, `visibility`, `whatever`
error: expected one of: `backtrace`, `context`, `crate_root`, `display`, `implicit`, `source`, `visibility`, `whatever`
--> $DIR/attribute-misuse.rs:5:13
|
5 | #[snafu(unknown_attribute)]
Expand Down
31 changes: 31 additions & 0 deletions snafu-derive/src/lib.rs
Expand Up @@ -41,6 +41,7 @@ struct EnumInfo {
struct FieldContainer {
name: syn::Ident,
backtrace_field: Option<Field>,
implicit_fields: Vec<Field>,
selector_kind: ContextSelectorKind,
display_format: Option<UserInput>,
doc_comment: String,
Expand Down Expand Up @@ -500,6 +501,11 @@ const ATTR_BACKTRACE_FALSE: WrongField = WrongField {
valid_field: "backtrace",
};

const ATTR_IMPLICIT: OnlyValidOn = OnlyValidOn {
attribute: "implicit",
valid_on: "enum variant or struct fields with a name",
};

const ATTR_VISIBILITY: OnlyValidOn = OnlyValidOn {
attribute: "visibility",
valid_on: "an enum, enum variants, or a struct with named fields",
Expand Down Expand Up @@ -554,6 +560,7 @@ fn parse_snafu_enum(
}
Att::CrateRoot(tokens, root) => crate_roots.add(root, tokens),
Att::Backtrace(tokens, ..) => enum_errors.add(tokens, ATTR_BACKTRACE),
Att::Implicit(tokens, ..) => enum_errors.add(tokens, ATTR_IMPLICIT),
Att::Context(tokens, ..) => enum_errors.add(tokens, ATTR_CONTEXT),
Att::Whatever(tokens) => enum_errors.add(tokens, ATTR_WHATEVER),
Att::DocComment(..) => { /* Just a regular doc comment. */ }
Expand Down Expand Up @@ -642,6 +649,7 @@ fn field_container(
Att::Whatever(tokens) => whatevers.add((), tokens),
Att::Source(tokens, ..) => outer_errors.add(tokens, ATTR_SOURCE),
Att::Backtrace(tokens, ..) => outer_errors.add(tokens, ATTR_BACKTRACE),
Att::Implicit(tokens, ..) => outer_errors.add(tokens, ATTR_IMPLICIT),
Att::CrateRoot(tokens, ..) => outer_errors.add(tokens, ATTR_CRATE_ROOT),
Att::DocComment(_tts, doc_comment_line) => {
// We join all the doc comment attributes with a space,
Expand All @@ -665,6 +673,7 @@ fn field_container(
let mut user_fields = Vec::new();
let mut source_fields = AtMostOne::new("source", inner_error_location);
let mut backtrace_fields = AtMostOne::new("backtrace", inner_error_location);
let mut implicit_fields = Vec::new();

for syn_field in fields {
let original = syn_field.clone();
Expand All @@ -689,6 +698,7 @@ fn field_container(
// don't need any more data.
let mut source_attrs = AtMostOne::new("source", ErrorLocation::OnField);
let mut backtrace_attrs = AtMostOne::new("backtrace", ErrorLocation::OnField);
let mut implicit_attrs = AtMostOne::new("implicit", ErrorLocation::OnField);

// Keep track of the negative markers so we can check for inconsistencies and
// exclude fields even if they have the "source" or "backtrace" name.
Expand Down Expand Up @@ -740,6 +750,11 @@ fn field_container(
field_errors.add(tokens, ATTR_BACKTRACE_FALSE);
}
}
Att::Implicit(tokens, v) => {
if v {
implicit_attrs.add((), tokens);
}
}
Att::Visibility(tokens, ..) => field_errors.add(tokens, ATTR_VISIBILITY),
Att::Display(tokens, ..) => field_errors.add(tokens, ATTR_DISPLAY),
Att::Context(tokens, ..) => field_errors.add(tokens, ATTR_CONTEXT),
Expand All @@ -755,6 +770,9 @@ fn field_container(
let (backtrace_attr, errs) = backtrace_attrs.finish_with_location();
errors.extend(errs);

let (implicit_attr, errs) = implicit_attrs.finish();
errors.extend(errs);

let source_attr = source_attr.or_else(|| {
if field.name == "source" && !source_opt_out {
Some((None, syn_field.clone().into_token_stream()))
Expand All @@ -771,6 +789,8 @@ fn field_container(
}
});

let implicit_attr = implicit_attr.is_some();

if let Some((maybe_transformation, location)) = source_attr {
let Field { name, ty, .. } = field;
let transformation = maybe_transformation
Expand All @@ -789,6 +809,8 @@ fn field_container(
);
} else if let Some((_, location)) = backtrace_attr {
backtrace_fields.add(field, location);
} else if implicit_attr {
implicit_fields.push(field);
} else {
user_fields.push(field);
}
Expand Down Expand Up @@ -906,6 +928,7 @@ fn field_container(
Ok(FieldContainer {
name,
backtrace_field: backtrace.map(|(val, _tts)| val),
implicit_fields,
selector_kind,
display_format,
doc_comment,
Expand Down Expand Up @@ -1010,6 +1033,7 @@ fn parse_snafu_tuple_struct(
}
}
Att::Backtrace(tokens, ..) => struct_errors.add(tokens, ATTR_BACKTRACE),
Att::Implicit(tokens, ..) => struct_errors.add(tokens, ATTR_IMPLICIT),
Att::Context(tokens, ..) => struct_errors.add(tokens, ATTR_CONTEXT),
Att::Whatever(tokens) => struct_errors.add(tokens, ATTR_CONTEXT),
Att::CrateRoot(tokens, root) => crate_roots.add(root, tokens),
Expand Down Expand Up @@ -1086,6 +1110,7 @@ enum SnafuAttribute {
CrateRoot(proc_macro2::TokenStream, UserInput),
Display(proc_macro2::TokenStream, UserInput),
DocComment(proc_macro2::TokenStream, String),
Implicit(proc_macro2::TokenStream, bool),
Source(proc_macro2::TokenStream, Vec<Source>),
Visibility(proc_macro2::TokenStream, UserInput),
Whatever(proc_macro2::TokenStream),
Expand Down Expand Up @@ -1275,6 +1300,7 @@ impl<'a> quote::ToTokens for ContextSelector<'a> {

let context_selector = ContextSelector {
backtrace_field: self.1.backtrace_field.as_ref(),
implicit_fields: &self.1.implicit_fields,
crate_root: &self.0.crate_root,
error_constructor_name: &quote! { #enum_name::#variant_name },
original_generics_without_defaults: &self.0.provided_generics_without_defaults(),
Expand Down Expand Up @@ -1306,6 +1332,7 @@ impl<'a> quote::ToTokens for DisplayImpl<'a> {
.map(|variant| {
let FieldContainer {
backtrace_field,
implicit_fields,
display_format,
doc_comment,
name: variant_name,
Expand All @@ -1315,6 +1342,7 @@ impl<'a> quote::ToTokens for DisplayImpl<'a> {

let arm = DisplayMatchArm {
backtrace_field: backtrace_field.as_ref(),
implicit_fields: &implicit_fields,
default_name: &variant_name,
display_format: display_format.as_ref().map(|f| &**f),
doc_comment,
Expand Down Expand Up @@ -1434,6 +1462,7 @@ impl NamedStructInfo {
name,
selector_kind,
backtrace_field,
implicit_fields,
display_format,
doc_comment,
visibility,
Expand Down Expand Up @@ -1489,6 +1518,7 @@ impl NamedStructInfo {

let arm = DisplayMatchArm {
backtrace_field: backtrace_field.as_ref(),
implicit_fields: &implicit_fields,
default_name: &name,
display_format: display_format.as_ref().map(|f| &**f),
doc_comment: &doc_comment,
Expand All @@ -1510,6 +1540,7 @@ impl NamedStructInfo {

let context_selector = ContextSelector {
backtrace_field: backtrace_field.as_ref(),
implicit_fields: implicit_fields,
crate_root: &crate_root,
error_constructor_name: &name,
original_generics_without_defaults: &original_generics,
Expand Down

0 comments on commit 4b5ebfc

Please sign in to comment.