Skip to content

Commit

Permalink
Add error checks for duplicate attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
tjkirch committed Jul 10, 2019
1 parent 56de99c commit 2f16731
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 5 deletions.
28 changes: 28 additions & 0 deletions compatibility-tests/compile-fail/tests/ui/attribute-duplication.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
mod enum_duplication {
use snafu::Snafu;

#[derive(Debug, Snafu)]
enum EnumError {
#[snafu(display("an error variant"))]
AVariant {
#[snafu(source(from(EnumError, Box::new)))]
#[snafu(backtrace(delegate))]
source: Box<EnumError>,
#[snafu(source)]
#[snafu(backtrace(delegate))]
source2: String,
#[snafu(source)]
#[snafu(backtrace(delegate))]
source3: String,

#[snafu(backtrace)]
backtrace1: String,
#[snafu(backtrace)]
backtrace2: String,
#[snafu(backtrace)]
backtrace3: String,
},
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
error: Only one field can be designated as 'source'; found a duplicate here:
--> $DIR/attribute-duplication.rs:16:13
|
16 | source3: String,
| ^^^^^^^

error: Only one field can be designated as 'source'; found a duplicate here:
--> $DIR/attribute-duplication.rs:13:13
|
13 | source2: String,
| ^^^^^^^

error: Only one field can be designated as 'backtrace'; found a duplicate here:
--> $DIR/attribute-duplication.rs:23:13
|
23 | backtrace3: String,
| ^^^^^^^^^^

error: Only one field can be designated as 'backtrace'; found a duplicate here:
--> $DIR/attribute-duplication.rs:21:13
|
21 | backtrace2: String,
| ^^^^^^^^^^

error: Only one field can be designated as 'backtrace(delegate)'; found a duplicate here:
--> $DIR/attribute-duplication.rs:16:13
|
16 | source3: String,
| ^^^^^^^

error: Only one field can be designated as 'backtrace(delegate)'; found a duplicate here:
--> $DIR/attribute-duplication.rs:13:13
|
13 | source2: String,
| ^^^^^^^

error: Cannot have 'backtrace' and 'backtrace(delegate)' fields in the same enum
--> $DIR/attribute-duplication.rs:19:13
|
19 | backtrace1: String,
| ^^^^^^^^^^

error: Cannot have 'backtrace' and 'backtrace(delegate)' fields in the same enum
--> $DIR/attribute-duplication.rs:10:13
|
10 | source: Box<EnumError>,
| ^^^^^^
65 changes: 60 additions & 5 deletions snafu-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl SyntaxErrors {

/// Adds a new syntax error. The given description will be used in the compile error pointing
/// to the given span. Helper structs are available to format common descriptions, e.g.
/// OnlyValidOn.
/// OnlyValidOn and DuplicateAttribute.
fn add<D>(&mut self, span: proc_macro2::Span, description: D)
where
D: fmt::Display,
Expand Down Expand Up @@ -153,6 +153,22 @@ impl fmt::Display for OnlyValidOn {
}
}

/// Helper structure to simplify parameters to SyntaxErrors.add, handling cases where an attribute
/// was incorrectly used multiple times on the same element.
struct DuplicateAttribute {
attribute: &'static str,
}

impl fmt::Display for DuplicateAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Only one field can be designated as '{}'; found a duplicate here:",
self.attribute
)
}
}

fn impl_snafu_macro(ty: syn::DeriveInput) -> TokenStream {
match parse_snafu_information(ty) {
Ok(info) => info.into(),
Expand Down Expand Up @@ -406,15 +422,54 @@ fn parse_snafu_enum(
}
}

// Reverse the lists of fields where we may find duplicates so that we can call the
// second (and later) items the "duplicates" without uglier indexing.
source_fields.reverse();
backtrace_fields.reverse();
backtrace_delegates.reverse();

let source_field = source_fields.pop();
// Report a warning if there are multiple?
for extra_field in source_fields.into_iter() {
errors.add(
extra_field.name.span(),
DuplicateAttribute {
attribute: "source",
},
);
}

let backtrace_field = backtrace_fields.pop();
// Report a warning if there are multiple?
for extra_field in backtrace_fields.into_iter() {
errors.add(
extra_field.name.span(),
DuplicateAttribute {
attribute: "backtrace",
},
);
}

let backtrace_delegate = backtrace_delegates.pop();
// Report a warning if there are multiple?
// Report a warning if delegating and our own?
for extra_field in backtrace_delegates.into_iter() {
errors.add(
extra_field.name.span(),
DuplicateAttribute {
attribute: "backtrace(delegate)",
},
);
}

if let (Some(backtrace_field), Some(backtrace_delegate)) =
(backtrace_field.as_ref(), backtrace_delegate.as_ref())
{
errors.add(
backtrace_field.name.span(),
"Cannot have 'backtrace' and 'backtrace(delegate)' fields in the same enum",
);
errors.add(
backtrace_delegate.name.span(),
"Cannot have 'backtrace' and 'backtrace(delegate)' fields in the same enum",
);
}

Ok(VariantInfo {
name,
Expand Down

0 comments on commit 2f16731

Please sign in to comment.