mirror of https://github.com/torvalds/linux.git
969 lines
33 KiB
Rust
969 lines
33 KiB
Rust
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
|
|
#[cfg(feature = "parsing")]
|
|
use crate::error::Result;
|
|
use crate::expr::Expr;
|
|
use crate::generics::TypeParamBound;
|
|
use crate::ident::Ident;
|
|
use crate::lifetime::Lifetime;
|
|
use crate::punctuated::Punctuated;
|
|
use crate::token;
|
|
use crate::ty::{ReturnType, Type};
|
|
|
|
ast_struct! {
|
|
/// A path at which a named item is exported (e.g. `std::collections::HashMap`).
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct Path {
|
|
pub leading_colon: Option<Token![::]>,
|
|
pub segments: Punctuated<PathSegment, Token![::]>,
|
|
}
|
|
}
|
|
|
|
impl<T> From<T> for Path
|
|
where
|
|
T: Into<PathSegment>,
|
|
{
|
|
fn from(segment: T) -> Self {
|
|
let mut path = Path {
|
|
leading_colon: None,
|
|
segments: Punctuated::new(),
|
|
};
|
|
path.segments.push_value(segment.into());
|
|
path
|
|
}
|
|
}
|
|
|
|
impl Path {
|
|
/// Determines whether this is a path of length 1 equal to the given
|
|
/// ident.
|
|
///
|
|
/// For them to compare equal, it must be the case that:
|
|
///
|
|
/// - the path has no leading colon,
|
|
/// - the number of path segments is 1,
|
|
/// - the first path segment has no angle bracketed or parenthesized
|
|
/// path arguments, and
|
|
/// - the ident of the first path segment is equal to the given one.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use proc_macro2::TokenStream;
|
|
/// use syn::{Attribute, Error, Meta, Result};
|
|
///
|
|
/// fn get_serde_meta_item(attr: &Attribute) -> Result<Option<&TokenStream>> {
|
|
/// if attr.path().is_ident("serde") {
|
|
/// match &attr.meta {
|
|
/// Meta::List(meta) => Ok(Some(&meta.tokens)),
|
|
/// bad => Err(Error::new_spanned(bad, "unrecognized attribute")),
|
|
/// }
|
|
/// } else {
|
|
/// Ok(None)
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
pub fn is_ident<I>(&self, ident: &I) -> bool
|
|
where
|
|
I: ?Sized,
|
|
Ident: PartialEq<I>,
|
|
{
|
|
match self.get_ident() {
|
|
Some(id) => id == ident,
|
|
None => false,
|
|
}
|
|
}
|
|
|
|
/// If this path consists of a single ident, returns the ident.
|
|
///
|
|
/// A path is considered an ident if:
|
|
///
|
|
/// - the path has no leading colon,
|
|
/// - the number of path segments is 1, and
|
|
/// - the first path segment has no angle bracketed or parenthesized
|
|
/// path arguments.
|
|
pub fn get_ident(&self) -> Option<&Ident> {
|
|
if self.leading_colon.is_none()
|
|
&& self.segments.len() == 1
|
|
&& self.segments[0].arguments.is_none()
|
|
{
|
|
Some(&self.segments[0].ident)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// An error if this path is not a single ident, as defined in `get_ident`.
|
|
#[cfg(feature = "parsing")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
pub fn require_ident(&self) -> Result<&Ident> {
|
|
self.get_ident().ok_or_else(|| {
|
|
crate::error::new2(
|
|
self.segments.first().unwrap().ident.span(),
|
|
self.segments.last().unwrap().ident.span(),
|
|
"expected this path to be an identifier",
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// A segment of a path together with any path arguments on that segment.
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct PathSegment {
|
|
pub ident: Ident,
|
|
pub arguments: PathArguments,
|
|
}
|
|
}
|
|
|
|
impl<T> From<T> for PathSegment
|
|
where
|
|
T: Into<Ident>,
|
|
{
|
|
fn from(ident: T) -> Self {
|
|
PathSegment {
|
|
ident: ident.into(),
|
|
arguments: PathArguments::None,
|
|
}
|
|
}
|
|
}
|
|
|
|
ast_enum! {
|
|
/// Angle bracketed or parenthesized arguments of a path segment.
|
|
///
|
|
/// ## Angle bracketed
|
|
///
|
|
/// The `<'a, T>` in `std::slice::iter<'a, T>`.
|
|
///
|
|
/// ## Parenthesized
|
|
///
|
|
/// The `(A, B) -> C` in `Fn(A, B) -> C`.
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub enum PathArguments {
|
|
None,
|
|
/// The `<'a, T>` in `std::slice::iter<'a, T>`.
|
|
AngleBracketed(AngleBracketedGenericArguments),
|
|
/// The `(A, B) -> C` in `Fn(A, B) -> C`.
|
|
Parenthesized(ParenthesizedGenericArguments),
|
|
}
|
|
}
|
|
|
|
impl Default for PathArguments {
|
|
fn default() -> Self {
|
|
PathArguments::None
|
|
}
|
|
}
|
|
|
|
impl PathArguments {
|
|
pub fn is_empty(&self) -> bool {
|
|
match self {
|
|
PathArguments::None => true,
|
|
PathArguments::AngleBracketed(bracketed) => bracketed.args.is_empty(),
|
|
PathArguments::Parenthesized(_) => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_none(&self) -> bool {
|
|
match self {
|
|
PathArguments::None => true,
|
|
PathArguments::AngleBracketed(_) | PathArguments::Parenthesized(_) => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
ast_enum! {
|
|
/// An individual generic argument, like `'a`, `T`, or `Item = T`.
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
#[non_exhaustive]
|
|
pub enum GenericArgument {
|
|
/// A lifetime argument.
|
|
Lifetime(Lifetime),
|
|
/// A type argument.
|
|
Type(Type),
|
|
/// A const expression. Must be inside of a block.
|
|
///
|
|
/// NOTE: Identity expressions are represented as Type arguments, as
|
|
/// they are indistinguishable syntactically.
|
|
Const(Expr),
|
|
/// A binding (equality constraint) on an associated type: the `Item =
|
|
/// u8` in `Iterator<Item = u8>`.
|
|
AssocType(AssocType),
|
|
/// An equality constraint on an associated constant: the `PANIC =
|
|
/// false` in `Trait<PANIC = false>`.
|
|
AssocConst(AssocConst),
|
|
/// An associated type bound: `Iterator<Item: Display>`.
|
|
Constraint(Constraint),
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
|
|
/// V>`.
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct AngleBracketedGenericArguments {
|
|
pub colon2_token: Option<Token![::]>,
|
|
pub lt_token: Token![<],
|
|
pub args: Punctuated<GenericArgument, Token![,]>,
|
|
pub gt_token: Token![>],
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// A binding (equality constraint) on an associated type: the `Item = u8`
|
|
/// in `Iterator<Item = u8>`.
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct AssocType {
|
|
pub ident: Ident,
|
|
pub generics: Option<AngleBracketedGenericArguments>,
|
|
pub eq_token: Token![=],
|
|
pub ty: Type,
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// An equality constraint on an associated constant: the `PANIC = false` in
|
|
/// `Trait<PANIC = false>`.
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct AssocConst {
|
|
pub ident: Ident,
|
|
pub generics: Option<AngleBracketedGenericArguments>,
|
|
pub eq_token: Token![=],
|
|
pub value: Expr,
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// An associated type bound: `Iterator<Item: Display>`.
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct Constraint {
|
|
pub ident: Ident,
|
|
pub generics: Option<AngleBracketedGenericArguments>,
|
|
pub colon_token: Token![:],
|
|
pub bounds: Punctuated<TypeParamBound, Token![+]>,
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) ->
|
|
/// C`.
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct ParenthesizedGenericArguments {
|
|
pub paren_token: token::Paren,
|
|
/// `(A, B)`
|
|
pub inputs: Punctuated<Type, Token![,]>,
|
|
/// `C`
|
|
pub output: ReturnType,
|
|
}
|
|
}
|
|
|
|
ast_struct! {
|
|
/// The explicit Self type in a qualified path: the `T` in `<T as
|
|
/// Display>::fmt`.
|
|
///
|
|
/// The actual path, including the trait and the associated item, is stored
|
|
/// separately. The `position` field represents the index of the associated
|
|
/// item qualified with this Self type.
|
|
///
|
|
/// ```text
|
|
/// <Vec<T> as a::b::Trait>::AssociatedItem
|
|
/// ^~~~~~ ~~~~~~~~~~~~~~^
|
|
/// ty position = 3
|
|
///
|
|
/// <Vec<T>>::AssociatedItem
|
|
/// ^~~~~~ ^
|
|
/// ty position = 0
|
|
/// ```
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
|
|
pub struct QSelf {
|
|
pub lt_token: Token![<],
|
|
pub ty: Box<Type>,
|
|
pub position: usize,
|
|
pub as_token: Option<Token![as]>,
|
|
pub gt_token: Token![>],
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "parsing")]
|
|
pub(crate) mod parsing {
|
|
use crate::error::Result;
|
|
#[cfg(feature = "full")]
|
|
use crate::expr::ExprBlock;
|
|
use crate::expr::{Expr, ExprPath};
|
|
use crate::ext::IdentExt as _;
|
|
#[cfg(feature = "full")]
|
|
use crate::generics::TypeParamBound;
|
|
use crate::ident::Ident;
|
|
use crate::lifetime::Lifetime;
|
|
use crate::lit::Lit;
|
|
use crate::parse::{Parse, ParseStream};
|
|
#[cfg(feature = "full")]
|
|
use crate::path::Constraint;
|
|
use crate::path::{
|
|
AngleBracketedGenericArguments, AssocConst, AssocType, GenericArgument,
|
|
ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf,
|
|
};
|
|
use crate::punctuated::Punctuated;
|
|
use crate::token;
|
|
use crate::ty::{ReturnType, Type};
|
|
#[cfg(not(feature = "full"))]
|
|
use crate::verbatim;
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for Path {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Self::parse_helper(input, false)
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for GenericArgument {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
if input.peek(Lifetime) && !input.peek2(Token![+]) {
|
|
return Ok(GenericArgument::Lifetime(input.parse()?));
|
|
}
|
|
|
|
if input.peek(Lit) || input.peek(token::Brace) {
|
|
return const_argument(input).map(GenericArgument::Const);
|
|
}
|
|
|
|
let mut argument: Type = input.parse()?;
|
|
|
|
match argument {
|
|
Type::Path(mut ty)
|
|
if ty.qself.is_none()
|
|
&& ty.path.leading_colon.is_none()
|
|
&& ty.path.segments.len() == 1
|
|
&& match &ty.path.segments[0].arguments {
|
|
PathArguments::None | PathArguments::AngleBracketed(_) => true,
|
|
PathArguments::Parenthesized(_) => false,
|
|
} =>
|
|
{
|
|
if let Some(eq_token) = input.parse::<Option<Token![=]>>()? {
|
|
let segment = ty.path.segments.pop().unwrap().into_value();
|
|
let ident = segment.ident;
|
|
let generics = match segment.arguments {
|
|
PathArguments::None => None,
|
|
PathArguments::AngleBracketed(arguments) => Some(arguments),
|
|
PathArguments::Parenthesized(_) => unreachable!(),
|
|
};
|
|
return if input.peek(Lit) || input.peek(token::Brace) {
|
|
Ok(GenericArgument::AssocConst(AssocConst {
|
|
ident,
|
|
generics,
|
|
eq_token,
|
|
value: const_argument(input)?,
|
|
}))
|
|
} else {
|
|
Ok(GenericArgument::AssocType(AssocType {
|
|
ident,
|
|
generics,
|
|
eq_token,
|
|
ty: input.parse()?,
|
|
}))
|
|
};
|
|
}
|
|
|
|
#[cfg(feature = "full")]
|
|
if let Some(colon_token) = input.parse::<Option<Token![:]>>()? {
|
|
let segment = ty.path.segments.pop().unwrap().into_value();
|
|
return Ok(GenericArgument::Constraint(Constraint {
|
|
ident: segment.ident,
|
|
generics: match segment.arguments {
|
|
PathArguments::None => None,
|
|
PathArguments::AngleBracketed(arguments) => Some(arguments),
|
|
PathArguments::Parenthesized(_) => unreachable!(),
|
|
},
|
|
colon_token,
|
|
bounds: {
|
|
let mut bounds = Punctuated::new();
|
|
loop {
|
|
if input.peek(Token![,]) || input.peek(Token![>]) {
|
|
break;
|
|
}
|
|
bounds.push_value({
|
|
let allow_precise_capture = false;
|
|
let allow_const = true;
|
|
TypeParamBound::parse_single(
|
|
input,
|
|
allow_precise_capture,
|
|
allow_const,
|
|
)?
|
|
});
|
|
if !input.peek(Token![+]) {
|
|
break;
|
|
}
|
|
let punct: Token![+] = input.parse()?;
|
|
bounds.push_punct(punct);
|
|
}
|
|
bounds
|
|
},
|
|
}));
|
|
}
|
|
|
|
argument = Type::Path(ty);
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
Ok(GenericArgument::Type(argument))
|
|
}
|
|
}
|
|
|
|
pub(crate) fn const_argument(input: ParseStream) -> Result<Expr> {
|
|
let lookahead = input.lookahead1();
|
|
|
|
if input.peek(Lit) {
|
|
let lit = input.parse()?;
|
|
return Ok(Expr::Lit(lit));
|
|
}
|
|
|
|
if input.peek(Ident) {
|
|
let ident: Ident = input.parse()?;
|
|
return Ok(Expr::Path(ExprPath {
|
|
attrs: Vec::new(),
|
|
qself: None,
|
|
path: Path::from(ident),
|
|
}));
|
|
}
|
|
|
|
if input.peek(token::Brace) {
|
|
#[cfg(feature = "full")]
|
|
{
|
|
let block: ExprBlock = input.parse()?;
|
|
return Ok(Expr::Block(block));
|
|
}
|
|
|
|
#[cfg(not(feature = "full"))]
|
|
{
|
|
let begin = input.fork();
|
|
let content;
|
|
braced!(content in input);
|
|
content.parse::<Expr>()?;
|
|
let verbatim = verbatim::between(&begin, input);
|
|
return Ok(Expr::Verbatim(verbatim));
|
|
}
|
|
}
|
|
|
|
Err(lookahead.error())
|
|
}
|
|
|
|
impl AngleBracketedGenericArguments {
|
|
/// Parse `::<…>` with mandatory leading `::`.
|
|
///
|
|
/// The ordinary [`Parse`] impl for `AngleBracketedGenericArguments`
|
|
/// parses optional leading `::`.
|
|
#[cfg(feature = "full")]
|
|
#[cfg_attr(docsrs, doc(cfg(all(feature = "parsing", feature = "full"))))]
|
|
pub fn parse_turbofish(input: ParseStream) -> Result<Self> {
|
|
let colon2_token: Token![::] = input.parse()?;
|
|
Self::do_parse(Some(colon2_token), input)
|
|
}
|
|
|
|
pub(crate) fn do_parse(
|
|
colon2_token: Option<Token![::]>,
|
|
input: ParseStream,
|
|
) -> Result<Self> {
|
|
Ok(AngleBracketedGenericArguments {
|
|
colon2_token,
|
|
lt_token: input.parse()?,
|
|
args: {
|
|
let mut args = Punctuated::new();
|
|
loop {
|
|
if input.peek(Token![>]) {
|
|
break;
|
|
}
|
|
let value: GenericArgument = input.parse()?;
|
|
args.push_value(value);
|
|
if input.peek(Token![>]) {
|
|
break;
|
|
}
|
|
let punct: Token![,] = input.parse()?;
|
|
args.push_punct(punct);
|
|
}
|
|
args
|
|
},
|
|
gt_token: input.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for AngleBracketedGenericArguments {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let colon2_token: Option<Token![::]> = input.parse()?;
|
|
Self::do_parse(colon2_token, input)
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for ParenthesizedGenericArguments {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
let content;
|
|
Ok(ParenthesizedGenericArguments {
|
|
paren_token: parenthesized!(content in input),
|
|
inputs: content.parse_terminated(Type::parse, Token![,])?,
|
|
output: input.call(ReturnType::without_plus)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
impl Parse for PathSegment {
|
|
fn parse(input: ParseStream) -> Result<Self> {
|
|
Self::parse_helper(input, false)
|
|
}
|
|
}
|
|
|
|
impl PathSegment {
|
|
fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> {
|
|
if input.peek(Token![super])
|
|
|| input.peek(Token![self])
|
|
|| input.peek(Token![crate])
|
|
|| cfg!(feature = "full") && input.peek(Token![try])
|
|
{
|
|
let ident = input.call(Ident::parse_any)?;
|
|
return Ok(PathSegment::from(ident));
|
|
}
|
|
|
|
let ident = if input.peek(Token![Self]) {
|
|
input.call(Ident::parse_any)?
|
|
} else {
|
|
input.parse()?
|
|
};
|
|
|
|
if !expr_style
|
|
&& input.peek(Token![<])
|
|
&& !input.peek(Token![<=])
|
|
&& !input.peek(Token![<<=])
|
|
|| input.peek(Token![::]) && input.peek3(Token![<])
|
|
{
|
|
Ok(PathSegment {
|
|
ident,
|
|
arguments: PathArguments::AngleBracketed(input.parse()?),
|
|
})
|
|
} else {
|
|
Ok(PathSegment::from(ident))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Path {
|
|
/// Parse a `Path` containing no path arguments on any of its segments.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// use syn::{Path, Result, Token};
|
|
/// use syn::parse::{Parse, ParseStream};
|
|
///
|
|
/// // A simplified single `use` statement like:
|
|
/// //
|
|
/// // use std::collections::HashMap;
|
|
/// //
|
|
/// // Note that generic parameters are not allowed in a `use` statement
|
|
/// // so the following must not be accepted.
|
|
/// //
|
|
/// // use a::<b>::c;
|
|
/// struct SingleUse {
|
|
/// use_token: Token![use],
|
|
/// path: Path,
|
|
/// }
|
|
///
|
|
/// impl Parse for SingleUse {
|
|
/// fn parse(input: ParseStream) -> Result<Self> {
|
|
/// Ok(SingleUse {
|
|
/// use_token: input.parse()?,
|
|
/// path: input.call(Path::parse_mod_style)?,
|
|
/// })
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
|
|
pub fn parse_mod_style(input: ParseStream) -> Result<Self> {
|
|
Ok(Path {
|
|
leading_colon: input.parse()?,
|
|
segments: {
|
|
let mut segments = Punctuated::new();
|
|
loop {
|
|
if !input.peek(Ident)
|
|
&& !input.peek(Token![super])
|
|
&& !input.peek(Token![self])
|
|
&& !input.peek(Token![Self])
|
|
&& !input.peek(Token![crate])
|
|
{
|
|
break;
|
|
}
|
|
let ident = Ident::parse_any(input)?;
|
|
segments.push_value(PathSegment::from(ident));
|
|
if !input.peek(Token![::]) {
|
|
break;
|
|
}
|
|
let punct = input.parse()?;
|
|
segments.push_punct(punct);
|
|
}
|
|
if segments.is_empty() {
|
|
return Err(input.parse::<Ident>().unwrap_err());
|
|
} else if segments.trailing_punct() {
|
|
return Err(input.error("expected path segment after `::`"));
|
|
}
|
|
segments
|
|
},
|
|
})
|
|
}
|
|
|
|
pub(crate) fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> {
|
|
let mut path = Path {
|
|
leading_colon: input.parse()?,
|
|
segments: {
|
|
let mut segments = Punctuated::new();
|
|
let value = PathSegment::parse_helper(input, expr_style)?;
|
|
segments.push_value(value);
|
|
segments
|
|
},
|
|
};
|
|
Path::parse_rest(input, &mut path, expr_style)?;
|
|
Ok(path)
|
|
}
|
|
|
|
pub(crate) fn parse_rest(
|
|
input: ParseStream,
|
|
path: &mut Self,
|
|
expr_style: bool,
|
|
) -> Result<()> {
|
|
while input.peek(Token![::]) && !input.peek3(token::Paren) {
|
|
let punct: Token![::] = input.parse()?;
|
|
path.segments.push_punct(punct);
|
|
let value = PathSegment::parse_helper(input, expr_style)?;
|
|
path.segments.push_value(value);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn is_mod_style(&self) -> bool {
|
|
self.segments
|
|
.iter()
|
|
.all(|segment| segment.arguments.is_none())
|
|
}
|
|
}
|
|
|
|
pub(crate) fn qpath(input: ParseStream, expr_style: bool) -> Result<(Option<QSelf>, Path)> {
|
|
if input.peek(Token![<]) {
|
|
let lt_token: Token![<] = input.parse()?;
|
|
let this: Type = input.parse()?;
|
|
let path = if input.peek(Token![as]) {
|
|
let as_token: Token![as] = input.parse()?;
|
|
let path: Path = input.parse()?;
|
|
Some((as_token, path))
|
|
} else {
|
|
None
|
|
};
|
|
let gt_token: Token![>] = input.parse()?;
|
|
let colon2_token: Token![::] = input.parse()?;
|
|
let mut rest = Punctuated::new();
|
|
loop {
|
|
let path = PathSegment::parse_helper(input, expr_style)?;
|
|
rest.push_value(path);
|
|
if !input.peek(Token![::]) {
|
|
break;
|
|
}
|
|
let punct: Token![::] = input.parse()?;
|
|
rest.push_punct(punct);
|
|
}
|
|
let (position, as_token, path) = match path {
|
|
Some((as_token, mut path)) => {
|
|
let pos = path.segments.len();
|
|
path.segments.push_punct(colon2_token);
|
|
path.segments.extend(rest.into_pairs());
|
|
(pos, Some(as_token), path)
|
|
}
|
|
None => {
|
|
let path = Path {
|
|
leading_colon: Some(colon2_token),
|
|
segments: rest,
|
|
};
|
|
(0, None, path)
|
|
}
|
|
};
|
|
let qself = QSelf {
|
|
lt_token,
|
|
ty: Box::new(this),
|
|
position,
|
|
as_token,
|
|
gt_token,
|
|
};
|
|
Ok((Some(qself), path))
|
|
} else {
|
|
let path = Path::parse_helper(input, expr_style)?;
|
|
Ok((None, path))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "printing")]
|
|
pub(crate) mod printing {
|
|
use crate::generics;
|
|
use crate::path::{
|
|
AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, GenericArgument,
|
|
ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf,
|
|
};
|
|
use crate::print::TokensOrDefault;
|
|
#[cfg(feature = "parsing")]
|
|
use crate::spanned::Spanned;
|
|
#[cfg(feature = "parsing")]
|
|
use proc_macro2::Span;
|
|
use proc_macro2::TokenStream;
|
|
use quote::ToTokens;
|
|
use std::cmp;
|
|
|
|
pub(crate) enum PathStyle {
|
|
Expr,
|
|
Mod,
|
|
AsWritten,
|
|
}
|
|
|
|
impl Copy for PathStyle {}
|
|
|
|
impl Clone for PathStyle {
|
|
fn clone(&self) -> Self {
|
|
*self
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for Path {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_path(tokens, self, PathStyle::AsWritten);
|
|
}
|
|
}
|
|
|
|
pub(crate) fn print_path(tokens: &mut TokenStream, path: &Path, style: PathStyle) {
|
|
path.leading_colon.to_tokens(tokens);
|
|
for segment in path.segments.pairs() {
|
|
print_path_segment(tokens, segment.value(), style);
|
|
segment.punct().to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for PathSegment {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_path_segment(tokens, self, PathStyle::AsWritten);
|
|
}
|
|
}
|
|
|
|
fn print_path_segment(tokens: &mut TokenStream, segment: &PathSegment, style: PathStyle) {
|
|
segment.ident.to_tokens(tokens);
|
|
print_path_arguments(tokens, &segment.arguments, style);
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for PathArguments {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_path_arguments(tokens, self, PathStyle::AsWritten);
|
|
}
|
|
}
|
|
|
|
fn print_path_arguments(tokens: &mut TokenStream, arguments: &PathArguments, style: PathStyle) {
|
|
match arguments {
|
|
PathArguments::None => {}
|
|
PathArguments::AngleBracketed(arguments) => {
|
|
print_angle_bracketed_generic_arguments(tokens, arguments, style);
|
|
}
|
|
PathArguments::Parenthesized(arguments) => {
|
|
print_parenthesized_generic_arguments(tokens, arguments, style);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for GenericArgument {
|
|
#[allow(clippy::match_same_arms)]
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
match self {
|
|
GenericArgument::Lifetime(lt) => lt.to_tokens(tokens),
|
|
GenericArgument::Type(ty) => ty.to_tokens(tokens),
|
|
GenericArgument::Const(expr) => {
|
|
generics::printing::print_const_argument(expr, tokens);
|
|
}
|
|
GenericArgument::AssocType(assoc) => assoc.to_tokens(tokens),
|
|
GenericArgument::AssocConst(assoc) => assoc.to_tokens(tokens),
|
|
GenericArgument::Constraint(constraint) => constraint.to_tokens(tokens),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for AngleBracketedGenericArguments {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_angle_bracketed_generic_arguments(tokens, self, PathStyle::AsWritten);
|
|
}
|
|
}
|
|
|
|
pub(crate) fn print_angle_bracketed_generic_arguments(
|
|
tokens: &mut TokenStream,
|
|
arguments: &AngleBracketedGenericArguments,
|
|
style: PathStyle,
|
|
) {
|
|
if let PathStyle::Mod = style {
|
|
return;
|
|
}
|
|
|
|
conditionally_print_turbofish(tokens, &arguments.colon2_token, style);
|
|
arguments.lt_token.to_tokens(tokens);
|
|
|
|
// Print lifetimes before types/consts/bindings, regardless of their
|
|
// order in args.
|
|
let mut trailing_or_empty = true;
|
|
for param in arguments.args.pairs() {
|
|
match param.value() {
|
|
GenericArgument::Lifetime(_) => {
|
|
param.to_tokens(tokens);
|
|
trailing_or_empty = param.punct().is_some();
|
|
}
|
|
GenericArgument::Type(_)
|
|
| GenericArgument::Const(_)
|
|
| GenericArgument::AssocType(_)
|
|
| GenericArgument::AssocConst(_)
|
|
| GenericArgument::Constraint(_) => {}
|
|
}
|
|
}
|
|
for param in arguments.args.pairs() {
|
|
match param.value() {
|
|
GenericArgument::Type(_)
|
|
| GenericArgument::Const(_)
|
|
| GenericArgument::AssocType(_)
|
|
| GenericArgument::AssocConst(_)
|
|
| GenericArgument::Constraint(_) => {
|
|
if !trailing_or_empty {
|
|
<Token![,]>::default().to_tokens(tokens);
|
|
}
|
|
param.to_tokens(tokens);
|
|
trailing_or_empty = param.punct().is_some();
|
|
}
|
|
GenericArgument::Lifetime(_) => {}
|
|
}
|
|
}
|
|
|
|
arguments.gt_token.to_tokens(tokens);
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for AssocType {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
self.ident.to_tokens(tokens);
|
|
self.generics.to_tokens(tokens);
|
|
self.eq_token.to_tokens(tokens);
|
|
self.ty.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for AssocConst {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
self.ident.to_tokens(tokens);
|
|
self.generics.to_tokens(tokens);
|
|
self.eq_token.to_tokens(tokens);
|
|
generics::printing::print_const_argument(&self.value, tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for Constraint {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
self.ident.to_tokens(tokens);
|
|
self.generics.to_tokens(tokens);
|
|
self.colon_token.to_tokens(tokens);
|
|
self.bounds.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
|
|
impl ToTokens for ParenthesizedGenericArguments {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
print_parenthesized_generic_arguments(tokens, self, PathStyle::AsWritten);
|
|
}
|
|
}
|
|
|
|
fn print_parenthesized_generic_arguments(
|
|
tokens: &mut TokenStream,
|
|
arguments: &ParenthesizedGenericArguments,
|
|
style: PathStyle,
|
|
) {
|
|
if let PathStyle::Mod = style {
|
|
return;
|
|
}
|
|
|
|
conditionally_print_turbofish(tokens, &None, style);
|
|
arguments.paren_token.surround(tokens, |tokens| {
|
|
arguments.inputs.to_tokens(tokens);
|
|
});
|
|
arguments.output.to_tokens(tokens);
|
|
}
|
|
|
|
pub(crate) fn print_qpath(
|
|
tokens: &mut TokenStream,
|
|
qself: &Option<QSelf>,
|
|
path: &Path,
|
|
style: PathStyle,
|
|
) {
|
|
let qself = match qself {
|
|
Some(qself) => qself,
|
|
None => {
|
|
print_path(tokens, path, style);
|
|
return;
|
|
}
|
|
};
|
|
qself.lt_token.to_tokens(tokens);
|
|
qself.ty.to_tokens(tokens);
|
|
|
|
let pos = cmp::min(qself.position, path.segments.len());
|
|
let mut segments = path.segments.pairs();
|
|
if pos > 0 {
|
|
TokensOrDefault(&qself.as_token).to_tokens(tokens);
|
|
path.leading_colon.to_tokens(tokens);
|
|
for (i, segment) in segments.by_ref().take(pos).enumerate() {
|
|
print_path_segment(tokens, segment.value(), PathStyle::AsWritten);
|
|
if i + 1 == pos {
|
|
qself.gt_token.to_tokens(tokens);
|
|
}
|
|
segment.punct().to_tokens(tokens);
|
|
}
|
|
} else {
|
|
qself.gt_token.to_tokens(tokens);
|
|
path.leading_colon.to_tokens(tokens);
|
|
}
|
|
for segment in segments {
|
|
print_path_segment(tokens, segment.value(), style);
|
|
segment.punct().to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
fn conditionally_print_turbofish(
|
|
tokens: &mut TokenStream,
|
|
colon2_token: &Option<Token![::]>,
|
|
style: PathStyle,
|
|
) {
|
|
match style {
|
|
PathStyle::Expr => TokensOrDefault(colon2_token).to_tokens(tokens),
|
|
PathStyle::Mod => unreachable!(),
|
|
PathStyle::AsWritten => colon2_token.to_tokens(tokens),
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "parsing")]
|
|
#[cfg_attr(docsrs, doc(cfg(all(feature = "parsing", feature = "printing"))))]
|
|
impl Spanned for QSelf {
|
|
fn span(&self) -> Span {
|
|
struct QSelfDelimiters<'a>(&'a QSelf);
|
|
|
|
impl<'a> ToTokens for QSelfDelimiters<'a> {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
self.0.lt_token.to_tokens(tokens);
|
|
self.0.gt_token.to_tokens(tokens);
|
|
}
|
|
}
|
|
|
|
QSelfDelimiters(self).span()
|
|
}
|
|
}
|
|
}
|