diff options
Diffstat (limited to 'vendor/clap_derive/src/utils')
-rw-r--r-- | vendor/clap_derive/src/utils/doc_comments.rs | 126 | ||||
-rw-r--r-- | vendor/clap_derive/src/utils/error.rs | 22 | ||||
-rw-r--r-- | vendor/clap_derive/src/utils/mod.rs | 13 | ||||
-rw-r--r-- | vendor/clap_derive/src/utils/spanned.rs | 89 | ||||
-rw-r--r-- | vendor/clap_derive/src/utils/ty.rs | 165 |
5 files changed, 415 insertions, 0 deletions
diff --git a/vendor/clap_derive/src/utils/doc_comments.rs b/vendor/clap_derive/src/utils/doc_comments.rs new file mode 100644 index 0000000..63c6ad1 --- /dev/null +++ b/vendor/clap_derive/src/utils/doc_comments.rs @@ -0,0 +1,126 @@ +//! The preprocessing we apply to doc comments. +//! +//! #[derive(Parser)] works in terms of "paragraphs". Paragraph is a sequence of +//! non-empty adjacent lines, delimited by sequences of blank (whitespace only) lines. + +use std::iter; + +pub fn extract_doc_comment(attrs: &[syn::Attribute]) -> Vec<String> { + // multiline comments (`/** ... */`) may have LFs (`\n`) in them, + // we need to split so we could handle the lines correctly + // + // we also need to remove leading and trailing blank lines + let mut lines: Vec<_> = attrs + .iter() + .filter(|attr| attr.path().is_ident("doc")) + .filter_map(|attr| { + // non #[doc = "..."] attributes are not our concern + // we leave them for rustc to handle + match &attr.meta { + syn::Meta::NameValue(syn::MetaNameValue { + value: + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(s), + .. + }), + .. + }) => Some(s.value()), + _ => None, + } + }) + .skip_while(|s| is_blank(s)) + .flat_map(|s| { + let lines = s + .split('\n') + .map(|s| { + // remove one leading space no matter what + let s = s.strip_prefix(' ').unwrap_or(s); + s.to_owned() + }) + .collect::<Vec<_>>(); + lines + }) + .collect(); + + while let Some(true) = lines.last().map(|s| is_blank(s)) { + lines.pop(); + } + + lines +} + +pub fn format_doc_comment( + lines: &[String], + preprocess: bool, + force_long: bool, +) -> (Option<String>, Option<String>) { + if let Some(first_blank) = lines.iter().position(|s| is_blank(s)) { + let (short, long) = if preprocess { + let paragraphs = split_paragraphs(lines); + let short = paragraphs[0].clone(); + let long = paragraphs.join("\n\n"); + (remove_period(short), long) + } else { + let short = lines[..first_blank].join("\n"); + let long = lines.join("\n"); + (short, long) + }; + + (Some(short), Some(long)) + } else { + let (short, long) = if preprocess { + let short = merge_lines(lines); + let long = force_long.then(|| short.clone()); + let short = remove_period(short); + (short, long) + } else { + let short = lines.join("\n"); + let long = force_long.then(|| short.clone()); + (short, long) + }; + + (Some(short), long) + } +} + +fn split_paragraphs(lines: &[String]) -> Vec<String> { + let mut last_line = 0; + iter::from_fn(|| { + let slice = &lines[last_line..]; + let start = slice.iter().position(|s| !is_blank(s)).unwrap_or(0); + + let slice = &slice[start..]; + let len = slice + .iter() + .position(|s| is_blank(s)) + .unwrap_or(slice.len()); + + last_line += start + len; + + if len != 0 { + Some(merge_lines(&slice[..len])) + } else { + None + } + }) + .collect() +} + +fn remove_period(mut s: String) -> String { + if s.ends_with('.') && !s.ends_with("..") { + s.pop(); + } + s +} + +fn is_blank(s: &str) -> bool { + s.trim().is_empty() +} + +fn merge_lines(lines: impl IntoIterator<Item = impl AsRef<str>>) -> String { + lines + .into_iter() + .map(|s| s.as_ref().trim().to_owned()) + .collect::<Vec<_>>() + .join(" ") +} diff --git a/vendor/clap_derive/src/utils/error.rs b/vendor/clap_derive/src/utils/error.rs new file mode 100644 index 0000000..276e349 --- /dev/null +++ b/vendor/clap_derive/src/utils/error.rs @@ -0,0 +1,22 @@ +pub trait SpanError { + #[allow(non_snake_case)] + fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error; +} + +pub trait ToTokensError { + #[allow(non_snake_case)] + fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error; +} + +impl<T: quote::ToTokens> ToTokensError for T { + fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error { + // Curb monomorphization from generating too many identical `new_spanned`. + syn::Error::new_spanned(self.to_token_stream(), msg) + } +} + +impl SpanError for proc_macro2::Span { + fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error { + syn::Error::new(*self, msg) + } +} diff --git a/vendor/clap_derive/src/utils/mod.rs b/vendor/clap_derive/src/utils/mod.rs new file mode 100644 index 0000000..13e6e71 --- /dev/null +++ b/vendor/clap_derive/src/utils/mod.rs @@ -0,0 +1,13 @@ +pub mod error; + +mod doc_comments; +mod spanned; +mod ty; + +pub use doc_comments::extract_doc_comment; +pub use doc_comments::format_doc_comment; + +pub use self::{ + spanned::Sp, + ty::{inner_type, is_simple_ty, sub_type, subty_if_name, Ty}, +}; diff --git a/vendor/clap_derive/src/utils/spanned.rs b/vendor/clap_derive/src/utils/spanned.rs new file mode 100644 index 0000000..339a654 --- /dev/null +++ b/vendor/clap_derive/src/utils/spanned.rs @@ -0,0 +1,89 @@ +use proc_macro2::{Ident, Span, TokenStream}; +use quote::ToTokens; +use syn::LitStr; + +use std::ops::{Deref, DerefMut}; + +/// An entity with a span attached. +#[derive(Debug, Copy, Clone)] +pub struct Sp<T> { + val: T, + span: Span, +} + +impl<T> Sp<T> { + pub fn new(val: T, span: Span) -> Self { + Sp { val, span } + } + + pub fn get(&self) -> &T { + &self.val + } + + pub fn span(&self) -> Span { + self.span + } +} + +impl<T> Deref for Sp<T> { + type Target = T; + + fn deref(&self) -> &T { + &self.val + } +} + +impl<T> DerefMut for Sp<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.val + } +} + +impl From<Ident> for Sp<String> { + fn from(ident: Ident) -> Self { + Sp { + val: ident.to_string(), + span: ident.span(), + } + } +} + +impl From<LitStr> for Sp<String> { + fn from(lit: LitStr) -> Self { + Sp { + val: lit.value(), + span: lit.span(), + } + } +} + +impl<'a> From<Sp<&'a str>> for Sp<String> { + fn from(sp: Sp<&'a str>) -> Self { + Sp::new(sp.val.into(), sp.span) + } +} + +impl<U, T: PartialEq<U>> PartialEq<U> for Sp<T> { + fn eq(&self, other: &U) -> bool { + self.val == *other + } +} + +impl<T: AsRef<str>> AsRef<str> for Sp<T> { + fn as_ref(&self) -> &str { + self.val.as_ref() + } +} + +impl<T: ToTokens> ToTokens for Sp<T> { + fn to_tokens(&self, stream: &mut TokenStream) { + // this is the simplest way out of correct ones to change span on + // arbitrary token tree I could come up with + let tt = self.val.to_token_stream().into_iter().map(|mut tt| { + tt.set_span(self.span); + tt + }); + + stream.extend(tt); + } +} diff --git a/vendor/clap_derive/src/utils/ty.rs b/vendor/clap_derive/src/utils/ty.rs new file mode 100644 index 0000000..9349bc2 --- /dev/null +++ b/vendor/clap_derive/src/utils/ty.rs @@ -0,0 +1,165 @@ +//! Special types handling + +use super::spanned::Sp; + +use syn::{ + spanned::Spanned, GenericArgument, Path, PathArguments, PathArguments::AngleBracketed, + PathSegment, Type, TypePath, +}; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Ty { + Unit, + Vec, + VecVec, + Option, + OptionOption, + OptionVec, + OptionVecVec, + Other, +} + +impl Ty { + pub fn from_syn_ty(ty: &syn::Type) -> Sp<Self> { + use self::Ty::*; + let t = |kind| Sp::new(kind, ty.span()); + + if is_unit_ty(ty) { + t(Unit) + } else if let Some(vt) = get_vec_ty(ty, Vec, VecVec) { + t(vt) + } else if let Some(subty) = subty_if_name(ty, "Option") { + if is_generic_ty(subty, "Option") { + t(OptionOption) + } else if let Some(vt) = get_vec_ty(subty, OptionVec, OptionVecVec) { + t(vt) + } else { + t(Option) + } + } else { + t(Other) + } + } + + pub fn as_str(&self) -> &'static str { + match self { + Self::Unit => "()", + Self::Vec => "Vec<T>", + Self::Option => "Option<T>", + Self::OptionOption => "Option<Option<T>>", + Self::OptionVec => "Option<Vec<T>>", + Self::VecVec => "Vec<Vec<T>>", + Self::OptionVecVec => "Option<Vec<Vec<T>>>", + Self::Other => "...other...", + } + } +} + +pub fn inner_type(field_ty: &syn::Type) -> &syn::Type { + let ty = Ty::from_syn_ty(field_ty); + match *ty { + Ty::Vec | Ty::Option => sub_type(field_ty).unwrap_or(field_ty), + Ty::OptionOption | Ty::OptionVec | Ty::VecVec => { + sub_type(field_ty).and_then(sub_type).unwrap_or(field_ty) + } + Ty::OptionVecVec => sub_type(field_ty) + .and_then(sub_type) + .and_then(sub_type) + .unwrap_or(field_ty), + _ => field_ty, + } +} + +pub fn sub_type(ty: &syn::Type) -> Option<&syn::Type> { + subty_if(ty, |_| true) +} + +fn only_last_segment(mut ty: &syn::Type) -> Option<&PathSegment> { + while let syn::Type::Group(syn::TypeGroup { elem, .. }) = ty { + ty = elem; + } + match ty { + Type::Path(TypePath { + qself: None, + path: + Path { + leading_colon: None, + segments, + }, + }) => only_one(segments.iter()), + + _ => None, + } +} + +fn subty_if<F>(ty: &syn::Type, f: F) -> Option<&syn::Type> +where + F: FnOnce(&PathSegment) -> bool, +{ + only_last_segment(ty) + .filter(|segment| f(segment)) + .and_then(|segment| { + if let AngleBracketed(args) = &segment.arguments { + only_one(args.args.iter()).and_then(|genneric| { + if let GenericArgument::Type(ty) = genneric { + Some(ty) + } else { + None + } + }) + } else { + None + } + }) +} + +pub fn subty_if_name<'a>(ty: &'a syn::Type, name: &str) -> Option<&'a syn::Type> { + subty_if(ty, |seg| seg.ident == name) +} + +pub fn is_simple_ty(ty: &syn::Type, name: &str) -> bool { + only_last_segment(ty) + .map(|segment| { + if let PathArguments::None = segment.arguments { + segment.ident == name + } else { + false + } + }) + .unwrap_or(false) +} + +fn is_generic_ty(ty: &syn::Type, name: &str) -> bool { + subty_if_name(ty, name).is_some() +} + +fn is_unit_ty(ty: &syn::Type) -> bool { + if let syn::Type::Tuple(tuple) = ty { + tuple.elems.is_empty() + } else { + false + } +} + +fn only_one<I, T>(mut iter: I) -> Option<T> +where + I: Iterator<Item = T>, +{ + iter.next().filter(|_| iter.next().is_none()) +} + +#[cfg(feature = "unstable-v5")] +fn get_vec_ty(ty: &Type, vec_ty: Ty, vecvec_ty: Ty) -> Option<Ty> { + subty_if_name(ty, "Vec").map(|subty| { + if is_generic_ty(subty, "Vec") { + vecvec_ty + } else { + vec_ty + } + }) +} + +#[cfg(not(feature = "unstable-v5"))] +fn get_vec_ty(ty: &Type, vec_ty: Ty, _vecvec_ty: Ty) -> Option<Ty> { + is_generic_ty(ty, "Vec").then_some(vec_ty) +} |