1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
use crate::ast::{Enum, Field, Input, Struct, Variant}; use crate::attr::Attrs; use quote::ToTokens; use std::collections::BTreeSet as Set; use syn::{Error, Member, Result}; impl Input<'_> { pub(crate) fn validate(&self) -> Result<()> { match self { Input::Struct(input) => input.validate(), Input::Enum(input) => input.validate(), } } } impl Struct<'_> { fn validate(&self) -> Result<()> { check_non_field_attrs(&self.attrs)?; if let Some(transparent) = self.attrs.transparent { if self.fields.len() != 1 { return Err(Error::new_spanned( transparent, "#[error(transparent)] requires exactly one field", )); } if let Some(source) = self.fields.iter().filter_map(|f| f.attrs.source).next() { return Err(Error::new_spanned( source, "transparent error struct can't contain #[source]", )); } } check_field_attrs(&self.fields)?; for field in &self.fields { field.validate()?; } Ok(()) } } impl Enum<'_> { fn validate(&self) -> Result<()> { check_non_field_attrs(&self.attrs)?; let has_display = self.has_display(); for variant in &self.variants { variant.validate()?; if has_display && variant.attrs.display.is_none() && variant.attrs.transparent.is_none() { return Err(Error::new_spanned( variant.original, "missing #[error(\"...\")] display attribute", )); } } let mut from_types = Set::new(); for variant in &self.variants { if let Some(from_field) = variant.from_field() { let repr = from_field.ty.to_token_stream().to_string(); if !from_types.insert(repr) { return Err(Error::new_spanned( from_field.original, "cannot derive From because another variant has the same source type", )); } } } Ok(()) } } impl Variant<'_> { fn validate(&self) -> Result<()> { check_non_field_attrs(&self.attrs)?; if self.attrs.transparent.is_some() { if self.fields.len() != 1 { return Err(Error::new_spanned( self.original, "#[error(transparent)] requires exactly one field", )); } if let Some(source) = self.fields.iter().filter_map(|f| f.attrs.source).next() { return Err(Error::new_spanned( source, "transparent variant can't contain #[source]", )); } } check_field_attrs(&self.fields)?; for field in &self.fields { field.validate()?; } Ok(()) } } impl Field<'_> { fn validate(&self) -> Result<()> { if let Some(display) = &self.attrs.display { return Err(Error::new_spanned( display.original, "not expected here; the #[error(...)] attribute belongs on top of a struct or an enum variant", )); } Ok(()) } } fn check_non_field_attrs(attrs: &Attrs) -> Result<()> { if let Some(from) = &attrs.from { return Err(Error::new_spanned( from, "not expected here; the #[from] attribute belongs on a specific field", )); } if let Some(source) = &attrs.source { return Err(Error::new_spanned( source, "not expected here; the #[source] attribute belongs on a specific field", )); } if let Some(backtrace) = &attrs.backtrace { return Err(Error::new_spanned( backtrace, "not expected here; the #[backtrace] attribute belongs on a specific field", )); } if let Some(display) = &attrs.display { if attrs.transparent.is_some() { return Err(Error::new_spanned( display.original, "cannot have both #[error(transparent)] and a display attribute", )); } } Ok(()) } fn check_field_attrs(fields: &[Field]) -> Result<()> { let mut from_field = None; let mut source_field = None; let mut backtrace_field = None; let mut has_backtrace = false; for field in fields { if let Some(from) = field.attrs.from { if from_field.is_some() { return Err(Error::new_spanned(from, "duplicate #[from] attribute")); } from_field = Some(field); } if let Some(source) = field.attrs.source { if source_field.is_some() { return Err(Error::new_spanned(source, "duplicate #[source] attribute")); } source_field = Some(field); } if let Some(backtrace) = field.attrs.backtrace { if backtrace_field.is_some() { return Err(Error::new_spanned( backtrace, "duplicate #[backtrace] attribute", )); } backtrace_field = Some(field); has_backtrace = true; } has_backtrace |= field.is_backtrace(); } if let (Some(from_field), Some(source_field)) = (from_field, source_field) { if !same_member(from_field, source_field) { return Err(Error::new_spanned( from_field.attrs.from, "#[from] is only supported on the source field, not any other field", )); } } if let Some(from_field) = from_field { if fields.len() > 1 + has_backtrace as usize { return Err(Error::new_spanned( from_field.attrs.from, "deriving From requires no fields other than source and backtrace", )); } } Ok(()) } fn same_member(one: &Field, two: &Field) -> bool { match (&one.member, &two.member) { (Member::Named(one), Member::Named(two)) => one == two, (Member::Unnamed(one), Member::Unnamed(two)) => one.index == two.index, _ => unreachable!(), } }