rust/crates/syntax/src/ast/node_ext.rs

655 lines
19 KiB
Rust
Raw Normal View History

2019-04-02 05:02:23 -05:00
//! Various extension methods to ast Nodes, which are hard to code-generate.
//! Extensions for various expressions live in a sibling `expr_extensions` module.
use std::fmt;
use itertools::Itertools;
2020-08-12 10:06:49 -05:00
use parser::SyntaxKind;
2019-05-20 17:04:20 -05:00
use crate::{
ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode},
2020-04-09 11:20:06 -05:00
SmolStr, SyntaxElement, SyntaxToken, T,
2019-05-20 17:04:20 -05:00
};
2019-04-02 04:47:39 -05:00
2020-12-15 12:23:51 -06:00
impl ast::Lifetime {
pub fn text(&self) -> SmolStr {
2020-12-15 12:23:51 -06:00
text_of_first_token(self.syntax())
}
}
2019-04-02 04:47:39 -05:00
impl ast::Name {
pub fn text(&self) -> SmolStr {
2019-07-18 11:23:05 -05:00
text_of_first_token(self.syntax())
2019-04-02 04:47:39 -05:00
}
}
impl ast::NameRef {
pub fn text(&self) -> SmolStr {
2019-07-18 11:23:05 -05:00
text_of_first_token(self.syntax())
}
pub fn as_tuple_field(&self) -> Option<usize> {
2020-04-10 04:49:13 -05:00
self.text().parse().ok()
}
2019-07-18 11:23:05 -05:00
}
fn text_of_first_token(node: &SyntaxNode) -> SmolStr {
node.green().children().next().and_then(|it| it.into_token()).unwrap().text().into()
2019-04-02 04:47:39 -05:00
}
2020-12-15 11:43:19 -06:00
pub enum Macro {
MacroRules(ast::MacroRules),
MacroDef(ast::MacroDef),
}
impl From<ast::MacroRules> for Macro {
fn from(it: ast::MacroRules) -> Self {
Macro::MacroRules(it)
}
}
impl From<ast::MacroDef> for Macro {
fn from(it: ast::MacroDef) -> Self {
Macro::MacroDef(it)
}
}
impl AstNode for Macro {
fn can_cast(kind: SyntaxKind) -> bool {
2021-03-21 09:33:18 -05:00
matches!(kind, SyntaxKind::MACRO_RULES | SyntaxKind::MACRO_DEF)
2020-12-15 11:43:19 -06:00
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
SyntaxKind::MACRO_RULES => Macro::MacroRules(ast::MacroRules { syntax }),
SyntaxKind::MACRO_DEF => Macro::MacroDef(ast::MacroDef { syntax }),
_ => return None,
};
Some(res)
}
fn syntax(&self) -> &SyntaxNode {
match self {
Macro::MacroRules(it) => it.syntax(),
Macro::MacroDef(it) => it.syntax(),
}
}
}
impl NameOwner for Macro {
fn name(&self) -> Option<ast::Name> {
match self {
Macro::MacroRules(mac) => mac.name(),
Macro::MacroDef(mac) => mac.name(),
}
}
}
impl AttrsOwner for Macro {}
/// Basically an owned `dyn AttrsOwner` without extra boxing.
pub struct AttrsOwnerNode {
node: SyntaxNode,
}
impl AttrsOwnerNode {
pub fn new<N: AttrsOwner>(node: N) -> Self {
AttrsOwnerNode { node: node.syntax().clone() }
}
}
impl AttrsOwner for AttrsOwnerNode {}
impl AstNode for AttrsOwnerNode {
fn can_cast(_: SyntaxKind) -> bool
where
Self: Sized,
{
false
}
fn cast(_: SyntaxNode) -> Option<Self>
where
Self: Sized,
{
None
}
fn syntax(&self) -> &SyntaxNode {
&self.node
}
}
2020-02-12 08:44:52 -06:00
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AttrKind {
Inner,
Outer,
}
2019-04-02 04:47:39 -05:00
impl ast::Attr {
2019-09-29 16:15:03 -05:00
pub fn as_simple_atom(&self) -> Option<SmolStr> {
2020-07-30 13:16:04 -05:00
if self.eq_token().is_some() || self.token_tree().is_some() {
return None;
2019-04-02 04:47:39 -05:00
}
2020-07-30 13:16:04 -05:00
self.simple_name()
2019-04-02 04:47:39 -05:00
}
2019-09-29 16:15:03 -05:00
pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
2020-07-30 13:16:04 -05:00
let tt = self.token_tree()?;
Some((self.simple_name()?, tt))
2019-04-02 04:47:39 -05:00
}
2019-09-29 16:15:03 -05:00
pub fn simple_name(&self) -> Option<SmolStr> {
let path = self.path()?;
match (path.segment(), path.qualifier()) {
2021-01-19 16:56:11 -06:00
(Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
2019-09-29 16:15:03 -05:00
_ => None,
2019-04-14 17:03:54 -05:00
}
}
2020-02-12 08:44:52 -06:00
pub fn kind(&self) -> AttrKind {
let first_token = self.syntax().first_token();
let first_token_kind = first_token.as_ref().map(SyntaxToken::kind);
let second_token_kind =
first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind);
2020-02-12 08:44:52 -06:00
match (first_token_kind, second_token_kind) {
(Some(T![#]), Some(T![!])) => AttrKind::Inner,
2020-02-12 08:44:52 -06:00
_ => AttrKind::Outer,
}
}
2019-04-02 04:47:39 -05:00
}
2019-07-18 11:23:05 -05:00
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathSegmentKind {
Name(ast::NameRef),
Type { type_ref: Option<ast::Type>, trait_ref: Option<ast::PathType> },
2019-04-02 04:47:39 -05:00
SelfKw,
SuperKw,
CrateKw,
}
impl ast::PathSegment {
2019-07-18 11:23:05 -05:00
pub fn parent_path(&self) -> ast::Path {
2019-04-02 04:47:39 -05:00
self.syntax()
.parent()
.and_then(ast::Path::cast)
.expect("segments are always nested in paths")
}
pub fn crate_token(&self) -> Option<SyntaxToken> {
self.name_ref().and_then(|it| it.crate_token())
}
pub fn self_token(&self) -> Option<SyntaxToken> {
self.name_ref().and_then(|it| it.self_token())
}
pub fn super_token(&self) -> Option<SyntaxToken> {
self.name_ref().and_then(|it| it.super_token())
}
2019-04-02 04:47:39 -05:00
pub fn kind(&self) -> Option<PathSegmentKind> {
let res = if let Some(name_ref) = self.name_ref() {
match name_ref.syntax().first_token().map(|it| it.kind()) {
Some(T![self]) => PathSegmentKind::SelfKw,
Some(T![super]) => PathSegmentKind::SuperKw,
Some(T![crate]) => PathSegmentKind::CrateKw,
_ => PathSegmentKind::Name(name_ref),
}
2019-04-02 04:47:39 -05:00
} else {
match self.syntax().first_child_or_token()?.kind() {
T![<] => {
// <T> or <T as Trait>
// T is any TypeRef, Trait has to be a PathType
let mut type_refs =
self.syntax().children().filter(|node| ast::Type::can_cast(node.kind()));
let type_ref = type_refs.next().and_then(ast::Type::cast);
let trait_ref = type_refs.next().and_then(ast::PathType::cast);
PathSegmentKind::Type { type_ref, trait_ref }
}
2019-04-02 04:47:39 -05:00
_ => return None,
}
};
Some(res)
}
}
impl ast::Path {
2019-07-18 11:23:05 -05:00
pub fn parent_path(&self) -> Option<ast::Path> {
2019-04-02 04:47:39 -05:00
self.syntax().parent().and_then(ast::Path::cast)
}
pub fn as_single_segment(&self) -> Option<ast::PathSegment> {
match self.qualifier() {
Some(_) => None,
None => self.segment(),
}
}
2019-04-02 04:47:39 -05:00
}
impl ast::UseTreeList {
2019-07-18 11:23:05 -05:00
pub fn parent_use_tree(&self) -> ast::UseTree {
2019-04-02 04:47:39 -05:00
self.syntax()
.parent()
.and_then(ast::UseTree::cast)
.expect("UseTreeLists are always nested in UseTrees")
}
2020-12-29 23:46:34 -06:00
pub fn has_inner_comment(&self) -> bool {
self.syntax()
.children_with_tokens()
2020-12-29 23:56:00 -06:00
.filter_map(|it| it.into_token())
.find_map(ast::Comment::cast)
.is_some()
2020-12-29 23:46:34 -06:00
}
2019-04-02 04:47:39 -05:00
}
2020-07-30 11:28:28 -05:00
impl ast::Impl {
2020-07-31 13:23:52 -05:00
pub fn self_ty(&self) -> Option<ast::Type> {
2019-04-02 04:47:39 -05:00
match self.target() {
(Some(t), None) | (_, Some(t)) => Some(t),
_ => None,
}
}
2020-07-31 13:23:52 -05:00
pub fn trait_(&self) -> Option<ast::Type> {
2019-04-02 04:47:39 -05:00
match self.target() {
(Some(t), Some(_)) => Some(t),
_ => None,
}
}
fn target(&self) -> (Option<ast::Type>, Option<ast::Type>) {
2020-04-09 15:22:58 -05:00
let mut types = support::children(self.syntax());
2019-04-02 04:47:39 -05:00
let first = types.next();
let second = types.next();
(first, second)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
2019-07-18 11:23:05 -05:00
pub enum StructKind {
2020-07-30 09:49:13 -05:00
Record(ast::RecordFieldList),
Tuple(ast::TupleFieldList),
2019-04-02 04:47:39 -05:00
Unit,
}
2019-07-18 11:23:05 -05:00
impl StructKind {
2019-04-02 04:48:14 -05:00
fn from_node<N: AstNode>(node: &N) -> StructKind {
2020-07-30 09:49:13 -05:00
if let Some(nfdl) = support::child::<ast::RecordFieldList>(node.syntax()) {
2019-11-22 12:52:06 -06:00
StructKind::Record(nfdl)
2020-07-30 09:49:13 -05:00
} else if let Some(pfl) = support::child::<ast::TupleFieldList>(node.syntax()) {
2019-04-02 04:48:14 -05:00
StructKind::Tuple(pfl)
2019-04-02 04:47:39 -05:00
} else {
2019-04-02 04:48:14 -05:00
StructKind::Unit
2019-04-02 04:47:39 -05:00
}
}
}
2020-07-30 10:50:40 -05:00
impl ast::Struct {
2019-04-02 04:48:14 -05:00
pub fn kind(&self) -> StructKind {
StructKind::from_node(self)
2019-04-02 04:47:39 -05:00
}
}
2020-07-30 09:21:30 -05:00
impl ast::RecordExprField {
pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> {
let candidate = Self::for_name_ref(field_name)?;
if candidate.field_name().as_ref() == Some(field_name) {
Some(candidate)
} else {
None
}
}
pub fn for_name_ref(name_ref: &ast::NameRef) -> Option<ast::RecordExprField> {
let syn = name_ref.syntax();
syn.parent()
.and_then(ast::RecordExprField::cast)
.or_else(|| syn.ancestors().nth(4).and_then(ast::RecordExprField::cast))
}
/// Deals with field init shorthand
pub fn field_name(&self) -> Option<ast::NameRef> {
if let Some(name_ref) = self.name_ref() {
return Some(name_ref);
}
self.expr()?.name_ref()
}
}
#[derive(Debug, Clone)]
pub enum NameLike {
NameRef(ast::NameRef),
Name(ast::Name),
Lifetime(ast::Lifetime),
}
impl NameLike {
pub fn as_name_ref(&self) -> Option<&ast::NameRef> {
match self {
NameLike::NameRef(name_ref) => Some(name_ref),
_ => None,
}
}
}
impl ast::AstNode for NameLike {
fn can_cast(kind: SyntaxKind) -> bool {
matches!(kind, SyntaxKind::NAME | SyntaxKind::NAME_REF | SyntaxKind::LIFETIME)
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
SyntaxKind::NAME => NameLike::Name(ast::Name { syntax }),
SyntaxKind::NAME_REF => NameLike::NameRef(ast::NameRef { syntax }),
SyntaxKind::LIFETIME => NameLike::Lifetime(ast::Lifetime { syntax }),
_ => return None,
};
Some(res)
}
fn syntax(&self) -> &SyntaxNode {
match self {
NameLike::NameRef(it) => it.syntax(),
NameLike::Name(it) => it.syntax(),
NameLike::Lifetime(it) => it.syntax(),
}
}
}
mod __ {
use super::{
ast::{Lifetime, Name, NameRef},
NameLike,
};
stdx::impl_from!(NameRef, Name, Lifetime for NameLike);
}
#[derive(Debug, Clone, PartialEq)]
pub enum NameOrNameRef {
Name(ast::Name),
NameRef(ast::NameRef),
}
impl fmt::Display for NameOrNameRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NameOrNameRef::Name(it) => fmt::Display::fmt(it, f),
NameOrNameRef::NameRef(it) => fmt::Display::fmt(it, f),
}
}
}
impl NameOrNameRef {
pub fn text(&self) -> SmolStr {
match self {
NameOrNameRef::Name(name) => name.text(),
NameOrNameRef::NameRef(name_ref) => name_ref.text(),
}
}
}
2020-07-31 12:54:16 -05:00
impl ast::RecordPatField {
pub fn for_field_name_ref(field_name: &ast::NameRef) -> Option<ast::RecordPatField> {
let candidate = field_name.syntax().parent().and_then(ast::RecordPatField::cast)?;
match candidate.field_name()? {
NameOrNameRef::NameRef(name_ref) if name_ref == *field_name => Some(candidate),
_ => None,
}
}
pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> {
let candidate =
field_name.syntax().ancestors().nth(2).and_then(ast::RecordPatField::cast)?;
match candidate.field_name()? {
NameOrNameRef::Name(name) if name == *field_name => Some(candidate),
_ => None,
}
}
/// Deals with field init shorthand
pub fn field_name(&self) -> Option<NameOrNameRef> {
if let Some(name_ref) = self.name_ref() {
return Some(NameOrNameRef::NameRef(name_ref));
}
match self.pat() {
Some(ast::Pat::IdentPat(pat)) => {
let name = pat.name()?;
Some(NameOrNameRef::Name(name))
}
Some(ast::Pat::BoxPat(pat)) => match pat.pat() {
Some(ast::Pat::IdentPat(pat)) => {
let name = pat.name()?;
Some(NameOrNameRef::Name(name))
}
_ => None,
},
_ => None,
}
}
}
2020-07-30 10:56:53 -05:00
impl ast::Variant {
2020-07-30 10:52:53 -05:00
pub fn parent_enum(&self) -> ast::Enum {
2019-04-02 04:47:39 -05:00
self.syntax()
.parent()
.and_then(|it| it.parent())
2020-07-30 10:52:53 -05:00
.and_then(ast::Enum::cast)
2019-04-02 04:47:39 -05:00
.expect("EnumVariants are always nested in Enums")
}
2019-04-02 04:48:14 -05:00
pub fn kind(&self) -> StructKind {
StructKind::from_node(self)
2019-04-02 04:47:39 -05:00
}
}
2019-04-05 15:34:45 -05:00
#[derive(Debug, Clone, PartialEq, Eq)]
2019-07-18 11:23:05 -05:00
pub enum FieldKind {
Name(ast::NameRef),
Index(SyntaxToken),
2019-04-05 15:34:45 -05:00
}
impl ast::FieldExpr {
pub fn index_token(&self) -> Option<SyntaxToken> {
self.syntax
.children_with_tokens()
// FIXME: Accepting floats here to reject them in validation later
.find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER)
.as_ref()
.and_then(SyntaxElement::as_token)
2019-07-18 11:23:05 -05:00
.cloned()
2019-04-05 15:34:45 -05:00
}
pub fn field_access(&self) -> Option<FieldKind> {
if let Some(nr) = self.name_ref() {
Some(FieldKind::Name(nr))
} else {
2021-03-21 09:33:18 -05:00
self.index_token().map(FieldKind::Index)
2019-04-05 15:34:45 -05:00
}
}
}
pub struct SlicePatComponents {
pub prefix: Vec<ast::Pat>,
pub slice: Option<ast::Pat>,
pub suffix: Vec<ast::Pat>,
}
impl ast::SlicePat {
pub fn components(&self) -> SlicePatComponents {
2020-07-31 14:58:36 -05:00
let mut args = self.pats().peekable();
let prefix = args
.peeking_take_while(|p| match p {
2020-07-31 14:45:29 -05:00
ast::Pat::RestPat(_) => false,
2021-03-21 09:33:18 -05:00
ast::Pat::IdentPat(bp) => !matches!(bp.pat(), Some(ast::Pat::RestPat(_))),
ast::Pat::RefPat(rp) => match rp.pat() {
2020-07-31 14:45:29 -05:00
Some(ast::Pat::RestPat(_)) => false,
2021-03-21 09:33:18 -05:00
Some(ast::Pat::IdentPat(bp)) => !matches!(bp.pat(), Some(ast::Pat::RestPat(_))),
_ => true,
},
_ => true,
})
.collect();
let slice = args.next();
let suffix = args.collect();
SlicePatComponents { prefix, slice, suffix }
}
}
2019-04-02 04:47:39 -05:00
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
2019-04-02 04:48:14 -05:00
pub enum SelfParamKind {
2019-04-02 04:47:39 -05:00
/// self
Owned,
/// &self
Ref,
/// &mut self
MutRef,
}
impl ast::SelfParam {
2019-04-02 04:48:14 -05:00
pub fn kind(&self) -> SelfParamKind {
if self.amp_token().is_some() {
2020-04-10 10:47:49 -05:00
if self.mut_token().is_some() {
2019-04-02 04:48:14 -05:00
SelfParamKind::MutRef
2019-04-02 04:47:39 -05:00
} else {
2019-04-02 04:48:14 -05:00
SelfParamKind::Ref
2019-04-02 04:47:39 -05:00
}
} else {
2019-04-02 04:48:14 -05:00
SelfParamKind::Owned
2019-04-02 04:47:39 -05:00
}
}
}
2019-08-22 10:43:09 -05:00
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeBoundKind {
/// Trait
PathType(ast::PathType),
/// for<'a> ...
ForType(ast::ForType),
/// 'a
2020-12-15 12:23:51 -06:00
Lifetime(ast::Lifetime),
2019-08-22 10:43:09 -05:00
}
impl ast::TypeBound {
2019-08-22 10:43:09 -05:00
pub fn kind(&self) -> TypeBoundKind {
2020-04-09 15:22:58 -05:00
if let Some(path_type) = support::children(self.syntax()).next() {
2019-08-22 10:43:09 -05:00
TypeBoundKind::PathType(path_type)
2020-04-09 15:22:58 -05:00
} else if let Some(for_type) = support::children(self.syntax()).next() {
2019-08-22 10:43:09 -05:00
TypeBoundKind::ForType(for_type)
2020-12-15 12:23:51 -06:00
} else if let Some(lifetime) = self.lifetime() {
2019-08-22 10:43:09 -05:00
TypeBoundKind::Lifetime(lifetime)
} else {
unreachable!()
}
}
}
pub enum VisibilityKind {
In(ast::Path),
PubCrate,
PubSuper,
PubSelf,
Pub,
}
impl ast::Visibility {
pub fn kind(&self) -> VisibilityKind {
match self.path() {
Some(path) => {
if let Some(segment) =
path.as_single_segment().filter(|it| it.coloncolon_token().is_none())
{
if segment.crate_token().is_some() {
return VisibilityKind::PubCrate;
} else if segment.super_token().is_some() {
return VisibilityKind::PubSuper;
} else if segment.self_token().is_some() {
return VisibilityKind::PubSelf;
}
}
VisibilityKind::In(path)
}
None => VisibilityKind::Pub,
}
}
}
2020-03-22 02:00:44 -05:00
impl ast::LifetimeParam {
pub fn lifetime_bounds(&self) -> impl Iterator<Item = SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.skip_while(|x| x.kind() != T![:])
2020-12-15 12:23:51 -06:00
.filter(|it| it.kind() == T![lifetime_ident])
}
}
impl ast::RangePat {
pub fn start(&self) -> Option<ast::Pat> {
self.syntax()
.children_with_tokens()
2020-04-10 04:49:13 -05:00
.take_while(|it| !(it.kind() == T![..] || it.kind() == T![..=]))
.filter_map(|it| it.into_node())
.find_map(ast::Pat::cast)
}
pub fn end(&self) -> Option<ast::Pat> {
self.syntax()
.children_with_tokens()
2020-04-10 04:49:13 -05:00
.skip_while(|it| !(it.kind() == T![..] || it.kind() == T![..=]))
.filter_map(|it| it.into_node())
.find_map(ast::Pat::cast)
}
}
impl ast::TokenTree {
2020-04-10 03:27:23 -05:00
pub fn left_delimiter_token(&self) -> Option<SyntaxToken> {
2020-06-27 20:02:03 -05:00
self.syntax()
.first_child_or_token()?
.into_token()
.filter(|it| matches!(it.kind(), T!['{'] | T!['('] | T!['[']))
2020-04-10 03:27:23 -05:00
}
pub fn right_delimiter_token(&self) -> Option<SyntaxToken> {
2020-06-27 20:02:03 -05:00
self.syntax()
.last_child_or_token()?
.into_token()
.filter(|it| matches!(it.kind(), T!['}'] | T![')'] | T![']']))
}
}
2020-07-30 11:52:02 -05:00
impl ast::GenericParamList {
pub fn lifetime_params(&self) -> impl Iterator<Item = ast::LifetimeParam> {
self.generic_params().filter_map(|param| match param {
ast::GenericParam::LifetimeParam(it) => Some(it),
ast::GenericParam::TypeParam(_) | ast::GenericParam::ConstParam(_) => None,
})
}
pub fn type_params(&self) -> impl Iterator<Item = ast::TypeParam> {
self.generic_params().filter_map(|param| match param {
ast::GenericParam::TypeParam(it) => Some(it),
ast::GenericParam::LifetimeParam(_) | ast::GenericParam::ConstParam(_) => None,
})
}
pub fn const_params(&self) -> impl Iterator<Item = ast::ConstParam> {
self.generic_params().filter_map(|param| match param {
ast::GenericParam::ConstParam(it) => Some(it),
ast::GenericParam::TypeParam(_) | ast::GenericParam::LifetimeParam(_) => None,
})
}
}
impl ast::DocCommentsOwner for ast::SourceFile {}
2020-07-30 07:51:08 -05:00
impl ast::DocCommentsOwner for ast::Fn {}
2020-07-30 10:50:40 -05:00
impl ast::DocCommentsOwner for ast::Struct {}
2020-07-30 10:36:46 -05:00
impl ast::DocCommentsOwner for ast::Union {}
2020-07-30 09:49:13 -05:00
impl ast::DocCommentsOwner for ast::RecordField {}
impl ast::DocCommentsOwner for ast::TupleField {}
2020-07-30 10:52:53 -05:00
impl ast::DocCommentsOwner for ast::Enum {}
2020-07-30 10:56:53 -05:00
impl ast::DocCommentsOwner for ast::Variant {}
2020-07-30 11:17:28 -05:00
impl ast::DocCommentsOwner for ast::Trait {}
impl ast::DocCommentsOwner for ast::Module {}
2020-07-30 11:02:20 -05:00
impl ast::DocCommentsOwner for ast::Static {}
impl ast::DocCommentsOwner for ast::Const {}
2020-07-30 08:25:46 -05:00
impl ast::DocCommentsOwner for ast::TypeAlias {}
2020-07-30 11:28:28 -05:00
impl ast::DocCommentsOwner for ast::Impl {}
2020-12-15 08:37:37 -06:00
impl ast::DocCommentsOwner for ast::MacroRules {}
2020-12-15 11:43:19 -06:00
impl ast::DocCommentsOwner for ast::MacroDef {}
impl ast::DocCommentsOwner for ast::Macro {}
2020-12-04 08:51:23 -06:00
impl ast::DocCommentsOwner for ast::Use {}