Skip to content

Commit

Permalink
Add support to parse dyn Trait.
Browse files Browse the repository at this point in the history
  • Loading branch information
azriel91 committed Oct 13, 2020
1 parent 1cccfa7 commit 2265f88
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 14 deletions.
14 changes: 12 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ where
mod tests {
use std::collections::HashMap;

use super::{type_name, type_namem, type_namemn, TypeName};
use super::{type_name, type_namem, type_namemn, type_namen, TypeName};

#[test]
fn type_name_primitives() {
Expand Down Expand Up @@ -245,6 +245,16 @@ mod tests {

#[test]
fn type_name_unsized() {
assert_eq!(type_name::<dyn std::fmt::Debug>(), "std::fmt::Debug");
assert_eq!(type_name::<dyn core::fmt::Debug>(), "dyn Debug");
}

#[test]
fn type_name_unsized_mn() {
assert_eq!(type_namem::<dyn core::fmt::Debug>(1), "dyn core::..::Debug");
assert_eq!(type_namen::<dyn core::fmt::Debug>(1), "dyn ..::fmt::Debug");
assert_eq!(
type_namemn::<dyn core::fmt::Debug>(0, 1),
"dyn ..::fmt::Debug"
);
}
}
73 changes: 61 additions & 12 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use nom::{
};

use crate::types::{
TypeName, TypeNameArray, TypeNameReference, TypeNameSlice, TypeNameStruct, TypeNameTuple,
TypeName, TypeNameArray, TypeNameReference, TypeNameSlice, TypeNameStruct, TypeNameTrait,
TypeNameTuple,
};

/// List of known primitive types
Expand Down Expand Up @@ -156,12 +157,7 @@ pub fn parse_unit_or_tuple(input: &str) -> IResult<&str, TypeName> {
alt((parse_unit, parse_tuple))(input)
}

pub fn named_primitive_or_struct(input: &str) -> IResult<&str, TypeName> {
// Check for primitive types first, otherwise we cannot distinguish the `std::u32` module from
// `u32` (type).
//
// We shouldn't have to worry about `use crate::u32; u32::SomeType` as the type name input
// should be the fully qualified crate name as determined by Rust.
pub fn named_primitive(input: &str) -> Option<IResult<&str, TypeName>> {
if let Some(prim_type) = PRIMITIVE_TYPES
.iter()
.find(|prim_type| input.starts_with(*prim_type))
Expand All @@ -177,32 +173,63 @@ pub fn named_primitive_or_struct(input: &str) -> IResult<&str, TypeName> {

if is_primitive_type {
// Treat this as a type.
return Ok((
Some(Ok((
remainder,
TypeName::Struct(TypeNameStruct {
module_path: vec![],
simple_name: prim_type,
type_params: vec![],
}),
));
)))
} else {
None
}
} else {
None
}
}

pub fn struct_type(input: &str) -> IResult<&str, TypeNameStruct> {
// Parse this as a module name
tuple((module_path, type_simple_name, type_parameters))(input).map(
|(s, (module_path, simple_name, type_params))| {
(
s,
TypeName::Struct(TypeNameStruct {
TypeNameStruct {
module_path,
simple_name,
type_params,
}),
},
)
},
)
}

pub fn named_primitive_or_struct(input: &str) -> IResult<&str, TypeName> {
// Check for primitive types first, otherwise we cannot distinguish the `std::u32` module from
// `u32` (type).
//
// We shouldn't have to worry about `use crate::u32; u32::SomeType` as the type name input
// should be the fully qualified crate name as determined by Rust.
if let Some(result) = named_primitive(input) {
result
} else {
struct_type(input)
.map(|(input, type_name_struct)| (input, TypeName::Struct(type_name_struct)))
}
}

pub fn trait_type(input: &str) -> IResult<&str, TypeName> {
struct_type(input).map(|(input, type_name_struct)| {
(
input,
TypeName::Trait(TypeNameTrait {
inner: type_name_struct,
}),
)
})
}

/// Parses a type name.
pub fn type_name(input: &str) -> IResult<&str, TypeName> {
// Primitive types begin with lowercase letters, but we have to detect them at this level, as
Expand All @@ -211,14 +238,36 @@ pub fn type_name(input: &str) -> IResult<&str, TypeName> {
//
// In addition, types may begin with symbols, and we should detect them here and branch to the
// relevant parsing functions.
if let Some(first_char) = input.chars().next() {
let mut chars = input.chars();
if let Some(first_char) = chars.next() {
match first_char {
'[' => array_or_slice(input),
'*' => unimplemented!("`tynm` is not implemented for pointer types."),
'!' => nom::character::complete::char('!')(input)
.map(|(input, _)| (input, TypeName::Never)),
'&' => parse_reference(input),
'(' => parse_unit_or_tuple(input),
'd' => {
let mut split = input.splitn(2, ' ');
if let Some("dyn") = split.next() {
if let Some(remainder) = split.next() {
trait_type(remainder)
} else {
// We only have "dyn" as a token. User may have specified r#dyn as a struct name or function
// name, but this is unusual. For now, we treat it as a struct.
Ok((
"",
TypeName::Struct(TypeNameStruct {
module_path: vec![],
simple_name: "dyn",
type_params: vec![],
}),
))
}
} else {
named_primitive_or_struct(input)
}
}
_ => named_primitive_or_struct(input),
}
} else {
Expand Down
90 changes: 90 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub enum TypeName<'s> {
Slice(TypeNameSlice<'s>),
Struct(TypeNameStruct<'s>),
Tuple(TypeNameTuple<'s>),
Trait(TypeNameTrait<'s>),
Unit,
}

Expand Down Expand Up @@ -157,6 +158,7 @@ impl<'s> TypeName<'s> {
Self::Slice(type_name_slice) => type_name_slice.write_str(buffer, m, n),
Self::Struct(type_name_struct) => type_name_struct.write_str(buffer, m, n),
Self::Tuple(type_name_tuple) => type_name_tuple.write_str(buffer, m, n),
Self::Trait(type_name_trait) => type_name_trait.write_str(buffer, m, n),
Self::Unit => buffer.write_str("()"),
}
}
Expand Down Expand Up @@ -510,6 +512,94 @@ impl<'s> TypeNameTuple<'s> {
}
}

/// Type name of a trait.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TypeNameTrait<'s> {
/// Share implementation with [`TypeNameStruct`]
pub(crate) inner: TypeNameStruct<'s>,
}

impl<'s> TypeNameTrait<'s> {
/// Returns the module path of the type.
pub fn module_path(&self) -> &[&'s str] {
&self.inner.module_path
}

/// Returns the simple name of the type, excluding type parameters.
pub fn simple_name(&self) -> &'s str {
self.inner.simple_name
}

/// Returns the type parameters of this type.
pub fn type_params(&self) -> &[TypeName<'s>] {
&self.inner.type_params
}

/// Writes the type name string to the given buffer.
///
/// If the left and right module segments overlap, the overlapping segments will only be printed
/// once.
///
/// # Parameters
///
/// * `buffer`: Buffer to write to.
/// * `m`: Number of module segments to include, beginning from the left (most significant).
/// * `n`: Number of module segments to include, beginning from the right (least significant).
pub fn write_str<W>(&self, buffer: &mut W, m: usize, n: usize) -> Result<(), Error>
where
W: Write,
{
buffer.write_str("dyn ")?;
self.inner.write_str(buffer, m, n)
}

/// Writes the module path to the given buffer.
///
/// If the left and right module segments overlap, the overlapping segments will only be printed
/// once.
///
/// # Parameters
///
/// * `buffer`: Buffer to write to.
/// * `m`: Number of module segments to include, beginning from the left (most significant).
/// * `n`: Number of module segments to include, beginning from the right (least significant).
pub fn write_module_path<W>(&self, buffer: &mut W, m: usize, n: usize) -> Result<(), Error>
where
W: Write,
{
self.inner.write_module_path(buffer, m, n)
}

/// Writes the simple name to the given buffer.
///
/// # Parameters
///
/// * `buffer`: Buffer to write to.
pub fn write_simple_name<W>(&self, buffer: &mut W) -> Result<(), Error>
where
W: Write,
{
self.inner.write_simple_name(buffer)
}

/// Writes type parameters to the given buffer.
///
/// If the left and right module segments overlap, the overlapping segments will only be printed
/// once.
///
/// # Parameters
///
/// * `buffer`: Buffer to write to.
/// * `m`: Number of module segments to include, beginning from the left (most significant).
/// * `n`: Number of module segments to include, beginning from the right (least significant).
pub fn write_type_params<W>(&self, buffer: &mut W, m: usize, n: usize) -> Result<(), Error>
where
W: Write,
{
self.inner.write_type_params(buffer, m, n)
}
}

impl<'s> From<&'s str> for TypeName<'s> {
fn from(std_type_name: &'s str) -> Self {
parser::type_name(std_type_name)
Expand Down

0 comments on commit 2265f88

Please sign in to comment.