use std::cmp::Ordering;
use std::collections::LinkedList;
use std::fmt;
use std::hash::{Hash, Hasher};
use crate::lex::Token;
use crate::{SourceCodeSpan, SourceSpan, source_code_span_impl};
use super::expr::Expr;
use super::punct::Punctuated;
use super::stmt::Block;
use super::ty::Type;
#[derive(Debug, Clone, Copy, Eq)]
pub struct Ident<'de> {
pub sym: &'de str,
pub span: SourceSpan<'de>,
}
source_code_span_impl!(Ident, Some, span);
impl<'de> PartialEq for Ident<'de> {
fn eq(&self, ident: &Ident<'de>) -> bool {
self.sym == ident.sym
}
}
impl<'de> PartialEq<str> for Ident<'de> {
fn eq(&self, sym: &str) -> bool {
self.sym == sym
}
}
impl<'de> PartialEq<Ident<'de>> for str {
fn eq(&self, ident: &Ident<'de>) -> bool {
self == ident.sym
}
}
impl<'de> PartialEq<&str> for Ident<'de> {
fn eq(&self, sym: &&str) -> bool {
self.sym == *sym
}
}
impl<'de> PartialEq<Ident<'de>> for &str {
fn eq(&self, ident: &Ident<'de>) -> bool {
*self == ident.sym
}
}
impl<'de> PartialEq<String> for Ident<'de> {
fn eq(&self, sym: &String) -> bool {
self.sym == sym.as_str()
}
}
impl<'de> PartialEq<Ident<'de>> for String {
fn eq(&self, ident: &Ident<'de>) -> bool {
self.as_str() == ident.sym
}
}
impl<'de> Ord for Ident<'de> {
fn cmp(&self, other: &Self) -> Ordering {
self.sym.cmp(other.sym)
}
}
impl<'de> PartialOrd for Ident<'de> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'de> Hash for Ident<'de> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.sym.hash(state);
}
}
impl<'de> fmt::Display for Ident<'de> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.sym)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum Visibility {
Public,
Protected,
Private,
#[default]
Inherited,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute<'de> {
pub span: SourceSpan<'de>,
pub path: Path<'de>,
pub args: Vec<Token<'de>>,
}
source_code_span_impl!(Attribute, Some, span);
#[derive(Debug, Clone, PartialEq)]
pub struct Path<'de> {
pub leading_colon: bool,
pub segments: Vec<PathSegment<'de>>,
}
source_code_span_impl!(Path, segments);
impl<'de> fmt::Display for Path<'de> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
self.segments
.iter()
.map(|s| s.ident.sym)
.collect::<Vec<_>>()
.join("::")
)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PathSegment<'de> {
pub ident: Ident<'de>,
}
source_code_span_impl!(PathSegment, ident);
#[derive(Debug, Clone, PartialEq)]
pub struct BaseSpecifier<'de> {
pub access: Visibility,
pub virtual_token: bool,
pub path: Path<'de>,
}
source_code_span_impl!(BaseSpecifier, path);
#[derive(Debug, Clone, PartialEq)]
pub struct Field<'de> {
pub attrs: Vec<Attribute<'de>>,
pub vis: Visibility,
pub static_token: bool,
pub ty: Type<'de>,
pub ident: Option<Ident<'de>>,
pub default_value: Option<Expr<'de>>,
}
source_code_span_impl!(Field, attrs, ty, and_then, ident, and_then, default_value);
#[derive(Debug, Clone, PartialEq)]
pub enum Fields<'de> {
Named(FieldsNamed<'de>),
Unit,
}
impl<'de> SourceCodeSpan<'de> for Fields<'de> {
fn span(&self) -> Option<SourceSpan<'de>> {
if let Fields::Named(fields_named) = self {
fields_named.span()
} else {
None
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FieldsNamed<'de> {
pub members: Vec<Member<'de>>,
}
source_code_span_impl!(FieldsNamed, members);
#[derive(Debug, Clone, PartialEq)]
pub enum Member<'de> {
AccessSpecifier(Visibility),
Field(Field<'de>),
Method(ItemFn<'de>),
Constructor(ItemConstructor<'de>),
Destructor(ItemDestructor<'de>),
Item(Box<Item<'de>>),
Friend(ItemFriend<'de>),
Using(ItemUse<'de>),
StaticAssert(ItemStaticAssert<'de>),
}
impl<'de> SourceCodeSpan<'de> for Member<'de> {
fn span(&self) -> Option<SourceSpan<'de>> {
match self {
Member::Field(field) => field.span(),
Member::Method(item_fn) => item_fn.span(),
Member::Constructor(item_constructor) => item_constructor.span(),
Member::Destructor(item_destructor) => item_destructor.span(),
Member::Item(item) => item.span(),
Member::Friend(item_friend) => item_friend.span(),
Member::Using(item_use) => item_use.span(),
Member::StaticAssert(item_static_assert) => item_static_assert.span(),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FnArg<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ty: Type<'de>,
pub ident: Option<Ident<'de>>,
pub default_value: Option<Expr<'de>>,
}
source_code_span_impl!(FnArg, attrs, ty, and_then, ident, and_then, default_value);
#[derive(Debug, Clone, PartialEq)]
pub struct Signature<'de> {
pub constexpr_token: bool,
pub consteval_token: bool,
pub inline_token: bool,
pub virtual_token: bool,
pub static_token: bool,
pub explicit_token: bool,
pub return_type: Type<'de>,
pub class_path: Option<Path<'de>>,
pub ident: Ident<'de>,
pub inputs: Punctuated<'de, FnArg<'de>>,
pub variadic: bool,
pub const_token: bool,
pub noexcept_token: bool,
pub override_token: bool,
pub final_token: bool,
pub pure_virtual: bool,
pub defaulted: bool,
pub deleted: bool,
pub destructor_token: bool,
pub member_init_list: Vec<MemberInit<'de>>,
}
source_code_span_impl!(
Signature,
return_type,
and_then,
class_path,
ident,
inputs,
member_init_list
);
impl<'de> Signature<'de> {
pub fn is_class_constructor(&self) -> bool {
!self.destructor_token
&& self
.class_path
.as_ref()
.is_some_and(|cp| self.ident == cp.to_string())
}
pub fn is_class_destructor(&self) -> bool {
self.destructor_token
}
pub fn has_no_required_params(&self) -> bool {
if let Some((fn_arg, _)) = self.inputs.inner.first()
&& fn_arg.default_value.is_none()
{
false
} else {
!self.deleted
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Variant<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ident: Ident<'de>,
pub discriminant: Option<Expr<'de>>,
}
source_code_span_impl!(Variant, attrs, ident, and_then, discriminant);
#[derive(Debug, Clone, PartialEq)]
pub enum TemplateParam<'de> {
Type {
ident: Option<Ident<'de>>,
default: Option<Type<'de>>,
},
NonType {
ty: Type<'de>,
ident: Option<Ident<'de>>,
default: Option<Expr<'de>>,
},
Template {
params: Vec<TemplateParam<'de>>,
ident: Option<Ident<'de>>,
},
Pack { ident: Ident<'de> },
}
impl<'de> SourceCodeSpan<'de> for TemplateParam<'de> {
fn span(&self) -> Option<SourceSpan<'de>> {
match self {
TemplateParam::Type { ident, default } => {
if let Some(ident_span) = ident.as_ref().map(|i| i.span) {
if let Some(default_span) = default.as_ref().and_then(|d| d.span()) {
Some(ident_span.extend(default_span))
} else {
Some(ident_span)
}
} else {
default.as_ref().and_then(|d| d.span())
}
}
TemplateParam::NonType { ty, ident, default } => {
let mut ret_span = ty.span();
if let Some(ident_span) = ident.as_ref().map(|i| i.span) {
ret_span = if let Some(span) = ret_span {
Some(span.extend(ident_span))
} else {
Some(ident_span)
}
}
if let Some(default_span) = default.as_ref().and_then(|d| d.span()) {
if let Some(span) = ret_span {
Some(span.extend(default_span))
} else {
Some(default_span)
}
} else {
ret_span
}
}
TemplateParam::Template { params, ident } => {
if let Some(ident_span) = ident.as_ref().map(|i| i.span) {
if let Some(params_span) = params.span() {
Some(ident_span.extend(params_span))
} else {
Some(ident_span)
}
} else {
params.span()
}
}
TemplateParam::Pack { ident } => Some(ident.span),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ForeignItem<'de> {
Fn(ItemFn<'de>),
Static(ItemStatic<'de>),
Verbatim(ItemVerbatim<'de>),
}
impl<'de> SourceCodeSpan<'de> for ForeignItem<'de> {
fn span(&self) -> Option<SourceSpan<'de>> {
match self {
ForeignItem::Fn(item_fn) => item_fn.span(),
ForeignItem::Static(item_static) => item_static.span(),
ForeignItem::Verbatim(item_verbatim) => item_verbatim.span(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MemberInit<'de> {
pub member: Path<'de>,
pub args: Punctuated<'de, Expr<'de>>,
}
source_code_span_impl!(MemberInit, member, args);
#[derive(Debug, Clone, PartialEq)]
pub enum Item<'de> {
Fn(ItemFn<'de>),
Struct(ItemStruct<'de>),
Class(ItemClass<'de>),
Enum(ItemEnum<'de>),
Union(ItemUnion<'de>),
Namespace(ItemNamespace<'de>),
Use(ItemUse<'de>),
Type(ItemType<'de>),
Typedef(ItemTypedef<'de>),
Const(ItemConst<'de>),
Static(ItemStatic<'de>),
Var(ItemVar<'de>),
ForeignMod(ItemForeignMod<'de>),
Template(ItemTemplate<'de>),
StaticAssert(ItemStaticAssert<'de>),
Include(ItemInclude<'de>),
Macro(ItemMacro<'de>),
Verbatim(ItemVerbatim<'de>),
}
impl<'de> SourceCodeSpan<'de> for Item<'de> {
fn span(&self) -> Option<SourceSpan<'de>> {
match self {
Item::Fn(item_fn) => item_fn.span(),
Item::Struct(item_struct) => item_struct.span(),
Item::Class(item_class) => item_class.span(),
Item::Enum(item_enum) => item_enum.span(),
Item::Union(item_union) => item_union.span(),
Item::Namespace(item_namespace) => item_namespace.span(),
Item::Use(item_use) => item_use.span(),
Item::Type(item_type) => item_type.span(),
Item::Typedef(item_typedef) => item_typedef.span(),
Item::Const(item_const) => item_const.span(),
Item::Static(item_static) => item_static.span(),
Item::Var(item_var) => item_var.span(),
Item::ForeignMod(item_foreign_mod) => item_foreign_mod.span(),
Item::Template(item_template) => item_template.span(),
Item::StaticAssert(item_static_assert) => item_static_assert.span(),
Item::Include(item_include) => item_include.span(),
Item::Macro(item_macro) => item_macro.span(),
Item::Verbatim(item_verbatim) => item_verbatim.span(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ItemFn<'de> {
pub attrs: Vec<Attribute<'de>>,
pub vis: Visibility,
pub sig: Signature<'de>,
pub block: Option<Block<'de>>,
}
source_code_span_impl!(ItemFn, attrs, sig, and_then, block);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemStruct<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ident: Option<Ident<'de>>,
pub generics: Option<Generics<'de>>,
pub bases: Vec<BaseSpecifier<'de>>,
pub fields: Fields<'de>,
}
source_code_span_impl!(
ItemStruct, attrs, and_then, ident, and_then, generics, bases, fields
);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemClass<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ident: Option<Ident<'de>>,
pub generics: Option<Generics<'de>>,
pub bases: Vec<BaseSpecifier<'de>>,
pub fields: Fields<'de>,
}
source_code_span_impl!(
ItemClass, attrs, and_then, ident, and_then, generics, bases, fields
);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemEnum<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ident: Option<Ident<'de>>,
pub scoped: bool,
pub underlying_type: Option<Type<'de>>,
pub variants: Punctuated<'de, Variant<'de>>,
}
source_code_span_impl!(
ItemEnum,
attrs,
and_then,
ident,
and_then,
underlying_type,
variants
);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemUnion<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ident: Option<Ident<'de>>,
pub fields: FieldsNamed<'de>,
}
source_code_span_impl!(ItemUnion, attrs, and_then, ident, fields);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemNamespace<'de> {
pub attrs: Vec<Attribute<'de>>,
pub inline_token: bool,
pub ident: Option<Ident<'de>>,
pub content: Vec<Item<'de>>,
}
source_code_span_impl!(ItemNamespace, attrs, and_then, ident, content);
#[derive(Debug, Clone, PartialEq)]
pub enum ItemUse<'de> {
Declaration {
attrs: Vec<Attribute<'de>>,
name: Path<'de>,
},
Directive {
attrs: Vec<Attribute<'de>>,
namespace: Path<'de>,
},
Alias {
attrs: Vec<Attribute<'de>>,
ident: Ident<'de>,
ty: Type<'de>,
},
}
impl<'de> SourceCodeSpan<'de> for ItemUse<'de> {
fn span(&self) -> Option<SourceSpan<'de>> {
match self {
ItemUse::Declaration { attrs, name } => {
if let Some(attrs_span) = attrs.span() {
if let Some(name_span) = name.span() {
Some(attrs_span.extend(name_span))
} else {
Some(attrs_span)
}
} else {
name.span()
}
}
ItemUse::Directive { attrs, namespace } => {
if let Some(attrs_span) = attrs.span() {
if let Some(namespace_span) = namespace.span() {
Some(attrs_span.extend(namespace_span))
} else {
Some(attrs_span)
}
} else {
namespace.span()
}
}
ItemUse::Alias { attrs, ident, ty } => {
if let Some(attrs_span) = attrs.span() {
if let Some(ty_span) = ty.span() {
Some(ident.span.extend(attrs_span.extend(ty_span)))
} else {
Some(ident.span.extend(attrs_span))
}
} else if let Some(ty_span) = ty.span() {
Some(ident.span.extend(ty_span))
} else {
Some(ident.span)
}
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ItemType<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ident: Ident<'de>,
pub generics: Option<Generics<'de>>,
pub ty: Type<'de>,
}
source_code_span_impl!(ItemType, attrs, ident, and_then, generics, ty);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemTypedef<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ty: Type<'de>,
pub ident: Ident<'de>,
}
source_code_span_impl!(ItemTypedef, attrs, ty, ident);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemConst<'de> {
pub attrs: Vec<Attribute<'de>>,
pub constexpr_token: bool,
pub ty: Type<'de>,
pub class_path: Option<Path<'de>>,
pub ident: Ident<'de>,
pub expr: Expr<'de>,
}
source_code_span_impl!(ItemConst, attrs, ty, and_then, class_path, ident, expr);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemStatic<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ty: Type<'de>,
pub class_path: Option<Path<'de>>,
pub ident: Ident<'de>,
pub expr: Option<Expr<'de>>,
}
source_code_span_impl!(
ItemStatic, attrs, ty, and_then, class_path, ident, and_then, expr
);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemVar<'de> {
pub attrs: Vec<Attribute<'de>>,
pub ty: Type<'de>,
pub ident: Ident<'de>,
pub expr: Option<Expr<'de>>,
}
source_code_span_impl!(ItemVar, attrs, ty, ident, and_then, expr);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemForeignMod<'de> {
pub attrs: Vec<Attribute<'de>>,
pub abi: &'de str,
pub items: Vec<ForeignItem<'de>>,
}
source_code_span_impl!(ItemForeignMod, attrs, items);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemTemplate<'de> {
pub attrs: Vec<Attribute<'de>>,
pub params: Punctuated<'de, TemplateParam<'de>>,
pub item: Box<Item<'de>>,
}
source_code_span_impl!(ItemTemplate, attrs, params, item);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemStaticAssert<'de> {
pub expr: Expr<'de>,
pub message: Option<Expr<'de>>,
}
source_code_span_impl!(ItemStaticAssert, expr, and_then, message);
#[derive(Debug, Clone, PartialEq)]
pub enum IncludePath<'de> {
System(SourceSpan<'de>),
Local(SourceSpan<'de>),
}
impl<'de> From<IncludePath<'de>> for SourceSpan<'de> {
fn from(include_path: IncludePath<'de>) -> Self {
match include_path {
IncludePath::System(source_span) | IncludePath::Local(source_span) => source_span,
}
}
}
impl<'de> From<&IncludePath<'de>> for SourceSpan<'de> {
fn from(include_path: &IncludePath<'de>) -> Self {
match include_path {
IncludePath::System(source_span) | IncludePath::Local(source_span) => *source_span,
}
}
}
impl<'de> SourceCodeSpan<'de> for IncludePath<'de> {
fn span(&self) -> Option<SourceSpan<'de>> {
Some(self.into())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ItemInclude<'de> {
pub span: SourceSpan<'de>,
pub path: IncludePath<'de>,
}
source_code_span_impl!(ItemInclude, Some, span, path);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemMacro<'de> {
pub span: SourceSpan<'de>,
pub tokens: Vec<Token<'de>>,
}
source_code_span_impl!(ItemMacro, Some, span, tokens);
#[derive(Debug, Default, Clone, PartialEq)]
pub struct ItemVerbatim<'de> {
pub tokens: LinkedList<Token<'de>>,
}
source_code_span_impl!(ItemVerbatim, tokens);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemConstructor<'de> {
pub attrs: Vec<Attribute<'de>>,
pub explicit_token: bool,
pub constexpr_token: bool,
pub ident: Ident<'de>,
pub inputs: Punctuated<'de, FnArg<'de>>,
pub noexcept_token: bool,
pub member_init_list: Vec<MemberInit<'de>>,
pub block: Option<Block<'de>>,
pub defaulted: bool,
pub deleted: bool,
}
impl<'de> ItemConstructor<'de> {
pub fn is_default_constructor(&self) -> bool {
if let Some((fn_arg, _)) = self.inputs.inner.first()
&& fn_arg.default_value.is_none()
{
false
} else {
!self.deleted
}
}
}
source_code_span_impl!(
ItemConstructor,
attrs,
ident,
inputs,
member_init_list,
and_then,
block
);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemDestructor<'de> {
pub attrs: Vec<Attribute<'de>>,
pub virtual_token: bool,
pub ident: Ident<'de>,
pub noexcept_token: bool,
pub block: Option<Block<'de>>,
pub defaulted: bool,
pub deleted: bool,
pub pure_virtual: bool,
}
source_code_span_impl!(ItemDestructor, attrs, ident, and_then, block);
#[derive(Debug, Clone, PartialEq)]
pub struct ItemFriend<'de> {
pub attrs: Vec<Attribute<'de>>,
pub item: Box<Item<'de>>,
}
source_code_span_impl!(ItemFriend, attrs, item);
#[derive(Debug, Clone, PartialEq)]
pub struct Generics<'de> {
pub params: Punctuated<'de, TemplateParam<'de>>,
}
source_code_span_impl!(Generics, params);