use proc_macro2::Span;
use quote::{quote, ToTokens};
use serde::{Deserialize, Serialize};
use std::fmt;
use super::{Attrs, Docs, Ident, Param, SelfParam, TraitSelfParam, TypeName};
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, PartialOrd, Ord)]
#[serde(transparent)]
pub struct NamedLifetime(Ident);
impl NamedLifetime {
pub fn name(&self) -> &Ident {
&self.0
}
}
impl<'de> Deserialize<'de> for NamedLifetime {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let named = Ident::deserialize(deserializer)?;
if named.as_str() == "static" {
panic!("cannot be static");
}
Ok(NamedLifetime(named))
}
}
impl From<&syn::Lifetime> for NamedLifetime {
fn from(lt: &syn::Lifetime) -> Self {
Lifetime::from(lt).to_named().expect("cannot be static")
}
}
impl From<&NamedLifetime> for NamedLifetime {
fn from(this: &NamedLifetime) -> Self {
this.clone()
}
}
impl PartialEq<syn::Lifetime> for NamedLifetime {
fn eq(&self, other: &syn::Lifetime) -> bool {
other.ident == self.0.as_str()
}
}
impl fmt::Display for NamedLifetime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "'{}", self.0)
}
}
impl ToTokens for NamedLifetime {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
use proc_macro2::{Punct, Spacing};
Punct::new('\'', Spacing::Joint).to_tokens(tokens);
self.0.to_tokens(tokens);
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct LifetimeEnv {
pub(crate) nodes: Vec<LifetimeNode>,
}
impl LifetimeEnv {
fn new() -> Self {
Self { nodes: vec![] }
}
pub fn names(&self) -> impl Iterator<Item = &NamedLifetime> + Clone {
self.nodes.iter().map(|node| &node.lifetime)
}
pub fn from_method_item(
method: &syn::ImplItemFn,
impl_generics: Option<&syn::Generics>,
self_param: Option<&SelfParam>,
params: &[Param],
return_type: Option<&TypeName>,
) -> Self {
let mut this = LifetimeEnv::new();
if let Some(generics) = impl_generics {
this.extend_generics(generics);
}
this.extend_generics(&method.sig.generics);
if let Some(self_param) = self_param {
this.extend_implicit_lifetime_bounds(&self_param.to_typename(), None);
}
for param in params {
this.extend_implicit_lifetime_bounds(¶m.ty, None);
}
if let Some(return_type) = return_type {
this.extend_implicit_lifetime_bounds(return_type, None);
}
this
}
pub fn from_trait_item(
trait_fct_item: &syn::TraitItem,
self_param: Option<&TraitSelfParam>,
params: &[Param],
return_type: Option<&TypeName>,
) -> Self {
let mut this = LifetimeEnv::new();
if let syn::TraitItem::Fn(_) = trait_fct_item {
if let Some(self_param) = self_param {
this.extend_implicit_lifetime_bounds(&self_param.to_typename(), None);
}
for param in params {
this.extend_implicit_lifetime_bounds(¶m.ty, None);
}
if let Some(return_type) = return_type {
this.extend_implicit_lifetime_bounds(return_type, None);
}
} else {
panic!(
"Diplomat traits can only have associated methods and no other associated items."
)
}
this
}
pub fn from_trait(trt: &syn::ItemTrait) -> Self {
if trt.generics.lifetimes().next().is_some() {
panic!("Diplomat traits are not allowed to have any lifetime parameters")
}
LifetimeEnv::new()
}
pub fn from_enum_item(
enm: &syn::ItemEnum,
variant_fields: &[(Option<Ident>, TypeName, Docs, Attrs)],
) -> Self {
let mut this = LifetimeEnv::new();
this.extend_generics(&enm.generics);
for (_, typ, _, _) in variant_fields {
this.extend_implicit_lifetime_bounds(typ, None);
}
this
}
pub fn from_struct_item(
strct: &syn::ItemStruct,
fields: &[(Ident, TypeName, Docs, Attrs)],
) -> Self {
let mut this = LifetimeEnv::new();
this.extend_generics(&strct.generics);
for (_, typ, _, _) in fields {
this.extend_implicit_lifetime_bounds(typ, None);
}
this
}
pub fn from_function_item(
f: &syn::ItemFn,
params: &[Param],
return_type: Option<&TypeName>,
) -> Self {
let mut this = LifetimeEnv::new();
this.extend_generics(&f.sig.generics);
for param in params {
this.extend_implicit_lifetime_bounds(¶m.ty, None);
}
if let Some(return_type) = return_type {
this.extend_implicit_lifetime_bounds(return_type, None);
}
this
}
fn extend_implicit_lifetime_bounds(
&mut self,
typ: &TypeName,
behind_ref: Option<&NamedLifetime>,
) {
match typ {
TypeName::Named(path_type) => {
if let Some(borrow_lifetime) = behind_ref {
let explicit_longer_than_borrow =
LifetimeTransitivity::longer_than(self, borrow_lifetime);
let mut implicit_longer_than_borrow = vec![];
for path_lifetime in path_type.lifetimes.iter() {
if let Lifetime::Named(path_lifetime) = path_lifetime {
if !explicit_longer_than_borrow.contains(&path_lifetime) {
implicit_longer_than_borrow.push(path_lifetime);
}
}
}
self.extend_bounds(
implicit_longer_than_borrow
.into_iter()
.map(|path_lifetime| (path_lifetime, Some(borrow_lifetime))),
);
}
}
TypeName::Reference(lifetime, _, typ) => {
let behind_ref = if let Lifetime::Named(named) = lifetime {
Some(named)
} else {
None
};
self.extend_implicit_lifetime_bounds(typ, behind_ref);
}
TypeName::Option(typ, _) => self.extend_implicit_lifetime_bounds(typ, None),
TypeName::Result(ok, err, _) => {
self.extend_implicit_lifetime_bounds(ok, None);
self.extend_implicit_lifetime_bounds(err, None);
}
_ => {}
}
}
fn extend_generics(&mut self, generics: &syn::Generics) {
let generic_bounds = generics.params.iter().map(|generic| match generic {
syn::GenericParam::Type(_) => panic!("generic types are unsupported"),
syn::GenericParam::Lifetime(def) => (&def.lifetime, &def.bounds),
syn::GenericParam::Const(_) => panic!("const generics are unsupported"),
});
let generic_defs = generic_bounds.clone().map(|(lifetime, _)| lifetime);
self.extend_lifetimes(generic_defs);
self.extend_bounds(generic_bounds);
if let Some(ref where_clause) = generics.where_clause {
self.extend_bounds(where_clause.predicates.iter().map(|pred| match pred {
syn::WherePredicate::Type(_) => panic!("trait bounds are unsupported"),
syn::WherePredicate::Lifetime(pred) => (&pred.lifetime, &pred.bounds),
_ => panic!("Found unknown kind of where predicate"),
}));
}
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn lifetimes_to_tokens(&self) -> proc_macro2::TokenStream {
if self.is_empty() {
return quote! {};
}
let lifetimes = self.nodes.iter().map(|node| &node.lifetime);
quote! { <#(#lifetimes),*> }
}
pub(crate) fn id<L>(&self, lifetime: &L) -> Option<usize>
where
NamedLifetime: PartialEq<L>,
{
self.nodes
.iter()
.position(|node| &node.lifetime == lifetime)
}
fn extend_lifetimes<'a, L, I>(&mut self, iter: I)
where
NamedLifetime: PartialEq<L> + From<&'a L>,
L: 'a,
I: IntoIterator<Item = &'a L>,
{
for lifetime in iter {
if self.id(lifetime).is_some() {
panic!(
"lifetime name `{}` declared twice in the same scope",
NamedLifetime::from(lifetime)
);
}
self.nodes.push(LifetimeNode {
lifetime: lifetime.into(),
shorter: vec![],
longer: vec![],
});
}
}
fn extend_bounds<'a, L, B, I>(&mut self, iter: I)
where
NamedLifetime: PartialEq<L> + From<&'a L>,
L: 'a,
B: IntoIterator<Item = &'a L>,
I: IntoIterator<Item = (&'a L, B)>,
{
for (lifetime, bounds) in iter {
let long = self.id(lifetime).expect("use of undeclared lifetime, this is a bug: try calling `LifetimeEnv::extend_lifetimes` first");
for bound in bounds {
let short = self
.id(bound)
.expect("cannot use undeclared lifetime as a bound");
self.nodes[short].longer.push(long);
self.nodes[long].shorter.push(short);
}
}
}
}
impl fmt::Display for LifetimeEnv {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_token_stream().fmt(f)
}
}
impl ToTokens for LifetimeEnv {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
for node in self.nodes.iter() {
let lifetime = &node.lifetime;
if node.shorter.is_empty() {
tokens.extend(quote! { #lifetime, });
} else {
let bounds = node.shorter.iter().map(|&id| &self.nodes[id].lifetime);
tokens.extend(quote! { #lifetime: #(#bounds)+*, });
}
}
}
}
impl Serialize for LifetimeEnv {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeMap;
let mut seq = serializer.serialize_map(Some(self.len()))?;
for node in self.nodes.iter() {
struct Bounds<'a> {
ids: &'a [usize],
nodes: &'a [LifetimeNode],
}
impl<'a> Serialize for Bounds<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(self.ids.len()))?;
for &id in self.ids {
seq.serialize_element(&self.nodes[id].lifetime)?;
}
seq.end()
}
}
seq.serialize_entry(
&node.lifetime,
&Bounds {
ids: &node.shorter[..],
nodes: &self.nodes,
},
)?;
}
seq.end()
}
}
impl<'de> Deserialize<'de> for LifetimeEnv {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use std::collections::BTreeMap;
let m: BTreeMap<NamedLifetime, Vec<NamedLifetime>> =
Deserialize::deserialize(deserializer)?;
let mut this = LifetimeEnv::new();
this.extend_lifetimes(m.keys());
this.extend_bounds(m.iter());
Ok(this)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct LifetimeNode {
pub(crate) lifetime: NamedLifetime,
pub(crate) shorter: Vec<usize>,
pub(crate) longer: Vec<usize>,
}
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Lifetime {
Static,
Named(NamedLifetime),
Anonymous,
}
impl Lifetime {
pub fn to_named(self) -> Option<NamedLifetime> {
if let Lifetime::Named(named) = self {
return Some(named);
}
None
}
pub fn as_named(&self) -> Option<&NamedLifetime> {
if let Lifetime::Named(named) = self {
return Some(named);
}
None
}
}
impl fmt::Display for Lifetime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Lifetime::Static => "'static".fmt(f),
Lifetime::Named(ref named) => named.fmt(f),
Lifetime::Anonymous => "'_".fmt(f),
}
}
}
impl ToTokens for Lifetime {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
Lifetime::Static => syn::Lifetime::new("'static", Span::call_site()).to_tokens(tokens),
Lifetime::Named(ref s) => s.to_tokens(tokens),
Lifetime::Anonymous => syn::Lifetime::new("'_", Span::call_site()).to_tokens(tokens),
};
}
}
impl From<&syn::Lifetime> for Lifetime {
fn from(lt: &syn::Lifetime) -> Self {
if lt.ident == "static" {
Self::Static
} else {
Self::Named(NamedLifetime((<.ident).into()))
}
}
}
impl From<&Option<syn::Lifetime>> for Lifetime {
fn from(lt: &Option<syn::Lifetime>) -> Self {
lt.as_ref().map(Into::into).unwrap_or(Self::Anonymous)
}
}
impl Lifetime {
pub fn to_syn(&self) -> Option<syn::Lifetime> {
match *self {
Self::Static => Some(syn::Lifetime::new("'static", Span::call_site())),
Self::Anonymous => None,
Self::Named(ref s) => Some(syn::Lifetime::new(&s.to_string(), Span::call_site())),
}
}
}
pub struct LifetimeTransitivity<'env> {
env: &'env LifetimeEnv,
visited: Vec<bool>,
out: Vec<&'env NamedLifetime>,
longer_or_shorter: LongerOrShorter,
}
impl<'env> LifetimeTransitivity<'env> {
pub fn longer(env: &'env LifetimeEnv) -> Self {
Self::new(env, LongerOrShorter::Longer)
}
pub fn shorter(env: &'env LifetimeEnv) -> Self {
Self::new(env, LongerOrShorter::Shorter)
}
pub fn longer_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'env NamedLifetime> {
let mut this = Self::new(env, LongerOrShorter::Longer);
this.visit(named);
this.finish()
}
pub fn shorter_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'env NamedLifetime> {
let mut this = Self::new(env, LongerOrShorter::Shorter);
this.visit(named);
this.finish()
}
fn new(env: &'env LifetimeEnv, longer_or_shorter: LongerOrShorter) -> Self {
LifetimeTransitivity {
env,
visited: vec![false; env.len()],
out: vec![],
longer_or_shorter,
}
}
pub fn visit(&mut self, named: &NamedLifetime) {
if let Some(id) = self
.env
.nodes
.iter()
.position(|node| node.lifetime == *named)
{
self.dfs(id);
}
}
fn dfs(&mut self, index: usize) {
if !self.visited[index] {
self.visited[index] = true;
let node = &self.env.nodes[index];
self.out.push(&node.lifetime);
for &edge_index in self.longer_or_shorter.edges(node).iter() {
self.dfs(edge_index);
}
}
}
pub fn finish(self) -> Vec<&'env NamedLifetime> {
self.out
}
}
enum LongerOrShorter {
Longer,
Shorter,
}
impl LongerOrShorter {
fn edges<'node>(&self, node: &'node LifetimeNode) -> &'node [usize] {
match self {
LongerOrShorter::Longer => &node.longer[..],
LongerOrShorter::Shorter => &node.shorter[..],
}
}
}