Skip to content

Commit

Permalink
avoid panicking on where constraints more complex than a single ident
Browse files Browse the repository at this point in the history
  • Loading branch information
cosmicexplorer committed Oct 8, 2021
1 parent eaa5111 commit 3b74bb0
Showing 1 changed file with 36 additions and 31 deletions.
67 changes: 36 additions & 31 deletions src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use syn::{
TypeParamBound, TypePath, WhereClause, WherePredicate,
};

use std::collections::HashMap;

pub(crate) fn derive(input: &DeriveInput) -> Result<TokenStream> {
let impls = match &input.data {
Data::Struct(data) => impl_struct(input, data),
Expand Down Expand Up @@ -201,8 +203,9 @@ fn add_display_constraint_to_type_predicate(
fn extract_trait_constraints_from_source(
where_clause: &WhereClause,
type_params: &[&TypeParam],
) -> Vec<(Ident, Vec<TraitBound>)> {
let mut param_constraint_mapping: Vec<(Ident, Vec<TraitBound>)> = type_params
) -> HashMap<Ident, Vec<TraitBound>> {
// Add trait bounds provided at the declaration site of type parameters for the struct/enum.
let mut param_constraint_mapping: HashMap<Ident, Vec<TraitBound>> = type_params
.iter()
.map(|type_param| {
let trait_bounds: Vec<TraitBound> = type_param
Expand All @@ -218,38 +221,38 @@ fn extract_trait_constraints_from_source(
})
.collect();

// Add trait bounds from `where` clauses, which may be type parameters or types containing
// those parameters.
for predicate in where_clause.predicates.iter() {
match predicate {
WherePredicate::Type(ref pred_ty) => {
let known_bounds: &mut Vec<TraitBound> = match &pred_ty.bounded_ty {
Type::Path(TypePath { path, qself: None }) => {
assert!(
path.leading_colon.is_none(),
"LHS of `where` boundary can't resolve from global scope!"
);
assert_eq!(
1,
path.segments.len(),
"LHS of `where` boundary can't have segments!"
);
let cur_segment = path.segments.first().unwrap();
assert_eq!(PathArguments::None, cur_segment.arguments);
let (_, ref mut known_bounds) = param_constraint_mapping
.iter_mut()
.find(|(id, _)| *id == cur_segment.ident)
.expect("expected all types in `where` boundaries to be declared!");
known_bounds
}
x => unreachable!("expected simple type in where clause, but got {:?}", x),
let ident = match &pred_ty.bounded_ty {
// We ignore any type constraints that aren't direct references to type
// parameters of the current enum of struct definition. No types can be
// constrained in a `where` clause unless they are a type parameter or a generic
// type instantiated with one of the type parameters, so by only allowing single
// identifiers, we can be sure that the constrained type is a type parameter
// that is contained in `param_constraint_mapping`.
Type::Path(TypePath { path, qself: None }) => match path.get_ident() {
None => continue,
Some(ident) => ident,
},
_ => continue,
};
for bound in pred_ty.bounds.iter() {
match bound {
TypeParamBound::Trait(ref bound) => {
known_bounds.push(bound.clone());
param_constraint_mapping
.iter_mut()
.find(|(id, _)| *id == ident)
.map(|(_, ref mut known_bounds)| {
for bound in pred_ty.bounds.iter() {
match bound {
TypeParamBound::Trait(ref bound) => {
known_bounds.push(bound.clone());
}
_ => (),
}
}
_ => (),
}
}
})
.expect("expected all types in `where` boundaries to be declared!");
}
_ => (),
}
Expand Down Expand Up @@ -297,15 +300,17 @@ fn ensure_display_in_where_clause_for_type(where_clause: &mut WhereClause, ident
})
}

/// Given all declared type parameters, ensure they all `impl`
/// a [core::fmt::Display]-like constraint.
/// Given all declared type parameters, add an `impl` for a [core::fmt::Display]-like constraint if
/// the type parameter is not otherwise constrained.
fn ensure_where_clause_has_display_for_all_members(
where_clause: &mut WhereClause,
type_params: &[&TypeParam],
) {
let param_constraint_mapping = extract_trait_constraints_from_source(where_clause, type_params);

for (ident, known_bounds) in param_constraint_mapping.into_iter() {
// If the type parameter has any constraints already, we don't want to touch it, to avoid
// breaking use cases where a type parameter only needs to impl `Debug`, for example.
if known_bounds.is_empty() {
ensure_display_in_where_clause_for_type(where_clause, ident);
}
Expand Down

0 comments on commit 3b74bb0

Please sign in to comment.