use std::fmt;
use super::ast::{Decl, Expr, QualType, Stmt, TranslationUnit, TypeNode};
use super::cpp_token::{AccessSpecifier, CppStandard};
#[derive(Debug, Clone, PartialEq)]
pub struct NestedNameSpecifier {
pub components: Vec<NestedNameComponent>,
}
impl NestedNameSpecifier {
pub fn new() -> Self {
Self {
components: Vec::new(),
}
}
pub fn push_namespace(&mut self, name: String) {
self.components.push(NestedNameComponent::Namespace(name));
}
pub fn push_type(&mut self, name: String) {
self.components.push(NestedNameComponent::Type(name));
}
pub fn is_empty(&self) -> bool {
self.components.is_empty()
}
}
impl fmt::Display for NestedNameSpecifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, comp) in self.components.iter().enumerate() {
if i > 0 {
write!(f, "::")?;
}
match comp {
NestedNameComponent::Namespace(n) => write!(f, "{}", n)?,
NestedNameComponent::Type(n) => write!(f, "{}", n)?,
NestedNameComponent::Template(n) => write!(f, "template {}", n)?,
NestedNameComponent::Global => {}
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum NestedNameComponent {
Namespace(String),
Type(String),
Template(String),
Global,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CXXName {
pub scope: Option<NestedNameSpecifier>,
pub name: String,
pub template_args: Option<Vec<TemplateArgument>>,
}
impl CXXName {
pub fn new(name: &str) -> Self {
Self {
scope: None,
name: name.to_string(),
template_args: None,
}
}
pub fn qualified(scope: NestedNameSpecifier, name: &str) -> Self {
Self {
scope: Some(scope),
name: name.to_string(),
template_args: None,
}
}
}
impl fmt::Display for CXXName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref scope) = self.scope {
write!(f, "{}::", scope)?;
}
write!(f, "{}", self.name)?;
if let Some(ref args) = self.template_args {
write!(f, "<")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
}
write!(f, ">")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TemplateArgument {
Type(QualType),
NonType(CXXExpr),
Template(CXXName),
}
impl fmt::Display for TemplateArgument {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TemplateArgument::Type(ty) => write!(f, "{}", ty),
TemplateArgument::NonType(e) => write!(f, "{}", e),
TemplateArgument::Template(n) => write!(f, "{}", n),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CXXExpr {
ThisExpr,
NullPtrLiteral,
BoolLiteral(bool),
IntegerLiteral(i64),
TypeidExpr {
operand: Box<CXXExpr>,
is_type: bool,
},
StaticCast {
target_ty: QualType,
expr: Box<CXXExpr>,
},
DynamicCast {
target_ty: QualType,
expr: Box<CXXExpr>,
},
ReinterpretCast {
target_ty: QualType,
expr: Box<CXXExpr>,
},
ConstCast {
target_ty: QualType,
expr: Box<CXXExpr>,
},
CXXNewExpr {
allocated_ty: QualType,
placement_args: Vec<CXXExpr>,
constructor_args: Vec<CXXExpr>,
is_array: bool,
array_size: Option<Box<CXXExpr>>,
},
CXXDeleteExpr {
operand: Box<CXXExpr>,
is_array: bool,
},
CXXThrowExpr(Option<Box<CXXExpr>>),
CXXMemberCall {
object: Box<CXXExpr>,
is_arrow: bool,
method: CXXName,
args: Vec<CXXExpr>,
},
CXXScopeExpr {
scope: NestedNameSpecifier,
member: String,
template_args: Option<Vec<TemplateArgument>>,
},
CXXMemberPtrAccess {
object: Box<CXXExpr>,
is_arrow: bool,
member_ptr: Box<CXXExpr>,
},
SpaceshipExpr {
lhs: Box<CXXExpr>,
rhs: Box<CXXExpr>,
},
LambdaExpr {
captures: Vec<LambdaCapture>,
params: Vec<CXXParamDecl>,
return_ty: Option<QualType>,
body: CXXCompoundStmt,
is_mutable: bool,
is_constexpr: bool,
is_generic: bool,
},
DecltypeExpr {
operand: Box<CXXExpr>,
},
NoexceptExpr {
operand: Box<CXXExpr>,
},
SizeofPackExpr {
pack_name: String,
},
FoldExpr {
operator: CXXOperator,
is_left_fold: bool,
init: Option<Box<CXXExpr>>,
pack: String,
},
RequiresExpr {
params: Vec<CXXParamDecl>,
requirements: Vec<Requirement>,
},
PackExpansion(Box<CXXExpr>),
StringLiteral(String),
FloatLiteral(f64),
CharLiteral(char),
MaterializeTemporaryExpr {
expr: Box<CXXExpr>,
lifetime_extended: bool,
},
CXXBindTemporaryExpr {
temp: Box<CXXExpr>,
dtor: Option<Box<CXXDecl>>,
},
CXXConstructExpr {
ty: QualType,
args: Vec<CXXExpr>,
is_list_init: bool,
is_elidable: bool,
},
CXXFunctionalCastExpr {
target_ty: QualType,
expr: Box<CXXExpr>,
},
CXXCStyleCastExpr {
target_ty: QualType,
expr: Box<CXXExpr>,
},
CXXInheritedCtorInitExpr {
class_name: String,
args: Vec<CXXExpr>,
},
CXXPseudoDestructorExpr {
base: Box<CXXExpr>,
destroyed_ty: QualType,
},
CXXOperatorCallExpr {
op: CXXOperator,
args: Vec<CXXExpr>,
},
TypeTraitExpr {
trait_name: String,
args: Vec<QualType>,
},
CExpr(Expr),
}
impl fmt::Display for CXXExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CXXExpr::ThisExpr => write!(f, "this"),
CXXExpr::NullPtrLiteral => write!(f, "nullptr"),
CXXExpr::BoolLiteral(true) => write!(f, "true"),
CXXExpr::BoolLiteral(false) => write!(f, "false"),
CXXExpr::IntegerLiteral(v) => write!(f, "{}", v),
CXXExpr::TypeidExpr { operand, .. } => write!(f, "typeid({})", operand),
CXXExpr::StaticCast { target_ty, expr } => {
write!(f, "static_cast<{}>({})", target_ty, expr)
}
CXXExpr::DynamicCast { target_ty, expr } => {
write!(f, "dynamic_cast<{}>({})", target_ty, expr)
}
CXXExpr::ReinterpretCast { target_ty, expr } => {
write!(f, "reinterpret_cast<{}>({})", target_ty, expr)
}
CXXExpr::ConstCast { target_ty, expr } => {
write!(f, "const_cast<{}>({})", target_ty, expr)
}
CXXExpr::CXXNewExpr { allocated_ty, .. } => write!(f, "new {}", allocated_ty),
CXXExpr::CXXDeleteExpr { operand, .. } => write!(f, "delete {}", operand),
CXXExpr::CXXThrowExpr(Some(e)) => write!(f, "throw {}", e),
CXXExpr::CXXThrowExpr(None) => write!(f, "throw"),
CXXExpr::CXXMemberCall {
object,
is_arrow,
method,
..
} => {
let op = if *is_arrow { "->" } else { "." };
write!(f, "{}{}{}", object, op, method)
}
CXXExpr::CXXScopeExpr { scope, member, .. } => write!(f, "{}::{}", scope, member),
CXXExpr::CXXMemberPtrAccess {
object, is_arrow, ..
} => {
let op = if *is_arrow { "->*" } else { ".*" };
write!(f, "{}{}", object, op)
}
CXXExpr::CXXMemberPtrAccess {
object,
is_arrow,
member_ptr,
} => {
let op = if *is_arrow { "->*" } else { ".*" };
write!(f, "{}{}{}", object, op, member_ptr)
}
CXXExpr::SpaceshipExpr { lhs, rhs } => write!(f, "{} <=> {}", lhs, rhs),
CXXExpr::LambdaExpr { .. } => write!(f, "<lambda>"),
CXXExpr::DecltypeExpr { operand } => write!(f, "decltype({})", operand),
CXXExpr::NoexceptExpr { operand } => write!(f, "noexcept({})", operand),
CXXExpr::SizeofPackExpr { pack_name } => write!(f, "sizeof...({})", pack_name),
CXXExpr::FoldExpr { pack, .. } => write!(f, "(... {} ...)", pack),
CXXExpr::RequiresExpr { .. } => write!(f, "<requires>"),
CXXExpr::PackExpansion(e) => write!(f, "{}...", e),
CXXExpr::StringLiteral(s) => write!(f, "\"{}\"", s),
CXXExpr::FloatLiteral(v) => write!(f, "{}", v),
CXXExpr::CharLiteral(c) => write!(f, "'{}'", c),
CXXExpr::MaterializeTemporaryExpr { expr, .. } => write!(f, "<materialize {}>", expr),
CXXExpr::CXXBindTemporaryExpr { temp, .. } => write!(f, "<bind {}>", temp),
CXXExpr::CXXConstructExpr { ty, .. } => write!(f, "<construct {}>", ty),
CXXExpr::CXXFunctionalCastExpr { target_ty, expr } => {
write!(f, "{}({})", target_ty, expr)
}
CXXExpr::CXXCStyleCastExpr { target_ty, expr } => write!(f, "({}){}", target_ty, expr),
CXXExpr::CXXInheritedCtorInitExpr { class_name, .. } => {
write!(f, "<inherited-ctor {}>", class_name)
}
CXXExpr::CXXPseudoDestructorExpr { base, destroyed_ty } => {
write!(f, "{}->~{}()", base, destroyed_ty)
}
CXXExpr::CXXOperatorCallExpr { op, args } => {
write!(f, "{}()", op.canonical_name())
}
CXXExpr::TypeTraitExpr { trait_name, .. } => write!(f, "{}()", trait_name),
CXXExpr::CExpr(e) => write!(f, "{}", e),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CaptureDefault {
ByCopy,
ByRef,
}
impl fmt::Display for CaptureDefault {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CaptureDefault::ByCopy => write!(f, "="),
CaptureDefault::ByRef => write!(f, "&"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LambdaCapture {
DefaultByValue,
DefaultByRef,
Capture {
name: String,
by_ref: bool,
capture_default: Option<CaptureDefault>,
init: Option<String>,
},
ThisByValue,
ThisByRef,
This,
ThisRef,
}
impl LambdaCapture {
pub fn default_capture(mode: CaptureDefault) -> Self {
match mode {
CaptureDefault::ByCopy => LambdaCapture::DefaultByValue,
CaptureDefault::ByRef => LambdaCapture::DefaultByRef,
}
}
pub fn variable(name: &str, by_ref: bool) -> Self {
LambdaCapture::Capture {
name: name.to_string(),
by_ref,
capture_default: None,
init: None,
}
}
pub fn set_init(&mut self, new_init: Option<String>) {
if let LambdaCapture::Capture { init, .. } = self {
*init = new_init;
}
}
}
impl fmt::Display for LambdaCapture {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LambdaCapture::DefaultByValue => write!(f, "="),
LambdaCapture::DefaultByRef => write!(f, "&"),
LambdaCapture::Capture {
name,
by_ref,
init: None,
..
} => {
if *by_ref {
write!(f, "&{}", name)
} else {
write!(f, "{}", name)
}
}
LambdaCapture::Capture {
name,
by_ref,
init: Some(init),
..
} => {
if *by_ref {
write!(f, "&{} = {}", name, init)
} else {
write!(f, "{} = {}", name, init)
}
}
LambdaCapture::ThisByValue | LambdaCapture::ThisRef => write!(f, "*this"),
LambdaCapture::ThisByRef | LambdaCapture::This => write!(f, "this"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CXXOperator {
Add,
Sub,
Mul,
Div,
Mod,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
Assign,
AddAssign,
SubAssign,
MulAssign,
DivAssign,
ModAssign,
AndAssign,
OrAssign,
XorAssign,
ShlAssign,
ShrAssign,
UnaryPlus,
UnaryMinus,
PreInc,
PostInc,
PreDec,
PostDec,
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
Spaceship,
LogicalNot,
LogicalAnd,
LogicalOr,
Subscript,
Arrow,
ArrowStar,
Deref,
Call,
Comma,
New,
NewArray,
Delete,
DeleteArray,
Literal,
Conversion,
Plus,
Minus,
Star,
Slash,
Percent,
Caret,
Ampersand,
Pipe,
Tilde,
Exclaim,
LShift,
RShift,
Less,
Greater,
EqEq,
NotEq,
LessEq,
GreaterEq,
AmpAmp,
PipePipe,
PlusEq,
MinusEq,
StarEq,
SlashEq,
PercentEq,
CaretEq,
AmpEq,
PipeEq,
LShiftEq,
RShiftEq,
PlusPlus,
MinusMinus,
Custom(String),
}
impl CXXOperator {
pub fn canonical_name(&self) -> String {
match self {
CXXOperator::Add | CXXOperator::Plus => "operator+".into(),
CXXOperator::Sub | CXXOperator::Minus => "operator-".into(),
CXXOperator::Mul | CXXOperator::Star => "operator*".into(),
CXXOperator::Div | CXXOperator::Slash => "operator/".into(),
CXXOperator::Mod | CXXOperator::Percent => "operator%".into(),
CXXOperator::BitAnd | CXXOperator::Ampersand => "operator&".into(),
CXXOperator::BitOr | CXXOperator::Pipe => "operator|".into(),
CXXOperator::BitXor | CXXOperator::Caret => "operator^".into(),
CXXOperator::Shl | CXXOperator::LShift => "operator<<".into(),
CXXOperator::Shr | CXXOperator::RShift => "operator>>".into(),
CXXOperator::Assign => "operator=".into(),
CXXOperator::AddAssign | CXXOperator::PlusEq => "operator+=".into(),
CXXOperator::SubAssign | CXXOperator::MinusEq => "operator-=".into(),
CXXOperator::MulAssign | CXXOperator::StarEq => "operator*=".into(),
CXXOperator::DivAssign | CXXOperator::SlashEq => "operator/=".into(),
CXXOperator::ModAssign | CXXOperator::PercentEq => "operator%=".into(),
CXXOperator::AndAssign | CXXOperator::AmpEq => "operator&=".into(),
CXXOperator::OrAssign | CXXOperator::PipeEq => "operator|=".into(),
CXXOperator::XorAssign | CXXOperator::CaretEq => "operator^=".into(),
CXXOperator::ShlAssign | CXXOperator::LShiftEq => "operator<<=".into(),
CXXOperator::ShrAssign | CXXOperator::RShiftEq => "operator>>=".into(),
CXXOperator::UnaryPlus => "operator+".into(),
CXXOperator::UnaryMinus => "operator-".into(),
CXXOperator::PreInc | CXXOperator::PlusPlus => "operator++".into(),
CXXOperator::PostInc => "operator++".into(),
CXXOperator::PreDec | CXXOperator::MinusMinus => "operator--".into(),
CXXOperator::PostDec => "operator--".into(),
CXXOperator::Eq | CXXOperator::EqEq => "operator==".into(),
CXXOperator::Ne | CXXOperator::NotEq => "operator!=".into(),
CXXOperator::Lt | CXXOperator::Less => "operator<".into(),
CXXOperator::Gt | CXXOperator::Greater => "operator>".into(),
CXXOperator::Le | CXXOperator::LessEq => "operator<=".into(),
CXXOperator::Ge | CXXOperator::GreaterEq => "operator>=".into(),
CXXOperator::Spaceship => "operator<=>".into(),
CXXOperator::LogicalNot | CXXOperator::Exclaim => "operator!".into(),
CXXOperator::LogicalAnd | CXXOperator::AmpAmp => "operator&&".into(),
CXXOperator::LogicalOr | CXXOperator::PipePipe => "operator||".into(),
CXXOperator::Subscript => "operator[]".into(),
CXXOperator::Arrow => "operator->".into(),
CXXOperator::ArrowStar => "operator->*".into(),
CXXOperator::Deref => "operator*".into(),
CXXOperator::Call => "operator()".into(),
CXXOperator::Comma => "operator,".into(),
CXXOperator::New => "operator new".into(),
CXXOperator::NewArray => "operator new[]".into(),
CXXOperator::Delete => "operator delete".into(),
CXXOperator::DeleteArray => "operator delete[]".into(),
CXXOperator::Literal => "operator\"\"".into(),
CXXOperator::Conversion => "operator".into(),
CXXOperator::Tilde => "operator~".into(),
CXXOperator::Custom(s) => format!("operator {}", s),
}
}
pub fn is_unary(&self) -> bool {
matches!(
self,
CXXOperator::UnaryPlus
| CXXOperator::UnaryMinus
| CXXOperator::LogicalNot
| CXXOperator::Deref
| CXXOperator::PreInc
| CXXOperator::PostInc
| CXXOperator::PreDec
| CXXOperator::PostDec
| CXXOperator::PlusPlus
| CXXOperator::MinusMinus
| CXXOperator::Exclaim
| CXXOperator::Tilde
| CXXOperator::Custom(_)
)
}
pub fn is_binary(&self) -> bool {
!self.is_unary()
&& !matches!(
self,
CXXOperator::Call
| CXXOperator::Subscript
| CXXOperator::New
| CXXOperator::NewArray
| CXXOperator::Delete
| CXXOperator::DeleteArray
| CXXOperator::Literal
| CXXOperator::Conversion
)
}
}
impl fmt::Display for CXXOperator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.canonical_name())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CXXAttribute {
pub namespace: Option<String>,
pub name: String,
pub args: Vec<String>,
}
impl fmt::Display for CXXAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[[")?;
if let Some(ref ns) = self.namespace {
write!(f, "{}::", ns)?;
}
write!(f, "{}", self.name)?;
if !self.args.is_empty() {
write!(f, "({})", self.args.join(", "))?;
}
write!(f, "]]")
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ExceptionDecl {
Ellipsis,
Typed { ty: QualType, name: Option<String> },
}
impl fmt::Display for ExceptionDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExceptionDecl::Ellipsis => write!(f, "..."),
ExceptionDecl::Typed { ty, name } => {
write!(f, "{}", ty)?;
if let Some(n) = name {
write!(f, " {}", n)?;
}
Ok(())
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CXXHandler {
pub exception_decl: ExceptionDecl,
pub body: CXXCompoundStmt,
}
impl fmt::Display for CXXHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "catch ({}) {{ ... }}", self.exception_decl)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CXXCatchHandler {
pub exception_type: Option<QualType>,
pub variable_name: Option<String>,
pub body: CXXCompoundStmt,
}
impl fmt::Display for CXXCatchHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match (&self.exception_type, &self.variable_name) {
(Some(ty), Some(name)) => write!(f, "catch ({} {}) {{ ... }}", ty, name),
(Some(ty), None) => write!(f, "catch ({}) {{ ... }}", ty),
(None, _) => write!(f, "catch (...) {{ ... }}"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Requirement {
Simple(CXXExpr),
Type { name: String },
Compound {
expr: Box<CXXExpr>,
is_noexcept: bool,
return_type_concept: Option<CXXName>,
},
Nested(Box<CXXExpr>),
}
impl fmt::Display for Requirement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Requirement::Simple(expr) => write!(f, "{};", expr),
Requirement::Type { name } => write!(f, "typename {};", name),
Requirement::Compound { expr, .. } => write!(f, "{{ {} }};", expr),
Requirement::Nested(e) => write!(f, "requires {};", e),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CXXParamDecl {
pub name: String,
pub ty: QualType,
pub has_default: bool,
pub default_value: Option<Box<CXXExpr>>,
pub is_parameter_pack: bool,
pub default: Option<String>,
pub is_pack: bool,
}
impl CXXParamDecl {
pub fn new(name: &str, ty: QualType) -> Self {
Self {
name: name.to_string(),
ty,
..Default::default()
}
}
}
impl Default for CXXParamDecl {
fn default() -> Self {
Self {
name: String::new(),
ty: QualType::void(),
has_default: false,
default_value: None,
is_parameter_pack: false,
default: None,
is_pack: false,
}
}
}
pub type CXXParam = CXXParamDecl;
#[derive(Debug, Clone, PartialEq)]
pub struct BaseSpecifier {
pub base_ty: CXXName,
pub is_virtual: bool,
pub access: AccessSpecifier,
}
impl BaseSpecifier {
pub fn public(name: &str) -> Self {
Self {
base_ty: CXXName::new(name),
is_virtual: false,
access: AccessSpecifier::Public,
}
}
pub fn protected(name: &str) -> Self {
Self {
base_ty: CXXName::new(name),
is_virtual: false,
access: AccessSpecifier::Protected,
}
}
pub fn private(name: &str) -> Self {
Self {
base_ty: CXXName::new(name),
is_virtual: false,
access: AccessSpecifier::Private,
}
}
}
impl fmt::Display for BaseSpecifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_virtual {
write!(f, "virtual ")?;
}
write!(f, "{} {}", self.access, self.base_ty)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CXXMemberDecl {
Field {
name: String,
ty: QualType,
is_mutable: bool,
is_static: bool,
bit_width: Option<Box<CXXExpr>>,
default_init: Option<Box<CXXExpr>>,
access: AccessSpecifier,
},
Method {
name: String,
return_ty: QualType,
params: Vec<CXXParamDecl>,
body: Option<CXXCompoundStmt>,
is_virtual: bool,
is_pure_virtual: bool,
is_override: bool,
is_final: bool,
is_const: bool,
is_volatile: bool,
is_static: bool,
is_noexcept: bool,
ref_qualifier: Option<RefQualifier>,
is_explicit: bool,
is_constexpr: bool,
is_consteval: bool,
trailing_return_ty: Option<QualType>,
access: AccessSpecifier,
},
Constructor {
params: Vec<CXXParamDecl>,
body: Option<CXXCompoundStmt>,
init_list: Vec<CtorInit>,
is_explicit: bool,
is_constexpr: bool,
is_default: bool,
is_deleted: bool,
access: AccessSpecifier,
},
Destructor {
body: Option<CXXCompoundStmt>,
is_virtual: bool,
is_default: bool,
is_deleted: bool,
access: AccessSpecifier,
},
NestedType {
name: String,
decl: Box<CXXDecl>,
access: AccessSpecifier,
},
FriendDecl {
decl: Box<CXXDecl>,
},
FriendClass {
class_name: CXXName,
access: AccessSpecifier,
},
FriendFunction {
name: CXXName,
params: Vec<CXXParamDecl>,
access: AccessSpecifier,
},
UsingDecl {
name: NestedNameSpecifier,
access: AccessSpecifier,
},
UsingAlias {
name: String,
aliased_ty: QualType,
access: AccessSpecifier,
},
OperatorDecl {
op: CXXOperator,
params: Vec<CXXParamDecl>,
body: Option<CXXCompoundStmt>,
is_const: bool,
is_noexcept: bool,
is_default: bool,
is_deleted: bool,
access: AccessSpecifier,
},
AccessSpec(AccessSpecifier),
StaticAssert {
condition: Box<CXXExpr>,
message: Option<String>,
},
}
impl fmt::Display for CXXMemberDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CXXMemberDecl::Field { name, ty, .. } => write!(f, "{} {}", ty, name),
CXXMemberDecl::Method {
name, return_ty, ..
} => write!(f, "{} {}", return_ty, name),
CXXMemberDecl::Constructor { .. } => write!(f, "<ctor>"),
CXXMemberDecl::Destructor { .. } => write!(f, "<dtor>"),
CXXMemberDecl::NestedType { name, .. } => write!(f, "<nested {}>", name),
CXXMemberDecl::FriendDecl { .. } => write!(f, "friend"),
CXXMemberDecl::FriendClass { class_name, .. } => {
write!(f, "friend class {}", class_name)
}
CXXMemberDecl::FriendFunction { name, .. } => write!(f, "friend {}", name),
CXXMemberDecl::UsingDecl { name, .. } => write!(f, "using {}", name),
CXXMemberDecl::UsingAlias {
name, aliased_ty, ..
} => write!(f, "using {} = {}", name, aliased_ty),
CXXMemberDecl::OperatorDecl { op, .. } => write!(f, "{}", op.canonical_name()),
CXXMemberDecl::AccessSpec(a) => write!(f, "{}:", a),
CXXMemberDecl::StaticAssert { .. } => write!(f, "static_assert"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RefQualifier {
LValue,
RValue,
}
impl fmt::Display for RefQualifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RefQualifier::LValue => write!(f, "&"),
RefQualifier::RValue => write!(f, "&&"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CtorInit {
Base {
name: CXXName,
args: Vec<CXXExpr>,
},
Member {
name: String,
init: Box<CXXExpr>,
},
Delegating {
args: Vec<CXXExpr>,
},
Simple {
member: String,
args: Vec<String>,
},
}
impl fmt::Display for CtorInit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CtorInit::Base { name, args } => {
write!(f, "{}(", name)?;
for (i, a) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", a)?;
}
write!(f, ")")
}
CtorInit::Member { name, init } => write!(f, "{}({})", name, init),
CtorInit::Delegating { args } => {
write!(f, "(")?;
for (i, a) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", a)?;
}
write!(f, ")")
}
CtorInit::Simple { member, args } => write!(f, "{}({})", member, args.join(", ")),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CXXCompoundStmt {
pub stmts: Vec<CXXStmt>,
}
impl CXXCompoundStmt {
pub fn new() -> Self {
Self { stmts: Vec::new() }
}
}
impl fmt::Display for CXXCompoundStmt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{{")?;
for s in &self.stmts {
writeln!(f, " {}", s)?;
}
write!(f, "}}")
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CXXStmt {
TryBlock {
body: CXXCompoundStmt,
handlers: Vec<CXXHandler>,
},
CXXTryStmt {
body: Box<CXXCompoundStmt>,
handlers: Vec<CXXCatchHandler>,
},
CXXThrowStmt(Option<Box<CXXExpr>>),
CXXExprStmt(CXXExpr),
ExprStmt(CXXExpr),
Compound(CXXCompoundStmt),
DeclStmt(CXXDecl),
ReturnStmt(Option<Box<CXXExpr>>),
IfStmt {
condition: Box<CXXExpr>,
then_branch: Box<CXXStmt>,
else_branch: Option<Box<CXXStmt>>,
init_stmt: Option<Box<CXXStmt>>,
is_constexpr: bool,
},
SwitchStmt {
condition: Box<CXXExpr>,
body: Box<CXXStmt>,
init_stmt: Option<Box<CXXStmt>>,
},
WhileStmt {
condition: Box<CXXExpr>,
body: Box<CXXStmt>,
},
DoWhileStmt {
body: Box<CXXStmt>,
condition: Box<CXXExpr>,
},
CXXForRangeStmt {
range_decl: Box<CXXDecl>,
range_init: Box<CXXExpr>,
body: Box<CXXStmt>,
},
ForStmt {
init: Option<Box<CXXStmt>>,
condition: Option<Box<CXXExpr>>,
increment: Option<Box<CXXExpr>>,
body: Box<CXXStmt>,
},
BreakStmt,
ContinueStmt,
GotoStmt(String),
LabelStmt(String, Box<CXXStmt>),
CaseStmt(CXXExpr, Box<CXXStmt>),
DefaultStmt(Box<CXXStmt>),
CoReturnStmt(Option<Box<CXXExpr>>),
CoYieldStmt(Box<CXXExpr>),
CoAwaitStmt(Box<CXXExpr>),
NullStmt,
}
impl fmt::Display for CXXStmt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CXXStmt::TryBlock { .. } => write!(f, "try {{ ... }}"),
CXXStmt::CXXTryStmt { .. } => write!(f, "try {{ ... }}"),
CXXStmt::CXXThrowStmt(Some(e)) => write!(f, "throw {};", e),
CXXStmt::CXXThrowStmt(None) => write!(f, "throw;"),
CXXStmt::CXXExprStmt(e) | CXXStmt::ExprStmt(e) => write!(f, "{};", e),
CXXStmt::Compound(c) => write!(f, "{}", c),
CXXStmt::DeclStmt(d) => write!(f, "{}", d),
CXXStmt::ReturnStmt(Some(e)) => write!(f, "return {};", e),
CXXStmt::ReturnStmt(None) => write!(f, "return;"),
CXXStmt::IfStmt { condition, .. } => write!(f, "if ({}) {{ ... }}", condition),
CXXStmt::SwitchStmt { condition, .. } => write!(f, "switch ({}) {{ ... }}", condition),
CXXStmt::WhileStmt { .. } => write!(f, "while (...) {{ ... }}"),
CXXStmt::DoWhileStmt { .. } => write!(f, "do {{ ... }} while (...)"),
CXXStmt::CXXForRangeStmt { .. } => write!(f, "for (auto x : range) {{ ... }}"),
CXXStmt::ForStmt { .. } => write!(f, "for (...) {{ ... }}"),
CXXStmt::BreakStmt => write!(f, "break;"),
CXXStmt::ContinueStmt => write!(f, "continue;"),
CXXStmt::GotoStmt(label) => write!(f, "goto {};", label),
CXXStmt::LabelStmt(label, _) => write!(f, "{}:", label),
CXXStmt::CaseStmt(val, _) => write!(f, "case {}:", val),
CXXStmt::DefaultStmt(_) => write!(f, "default:"),
CXXStmt::CoReturnStmt(_) => write!(f, "co_return;"),
CXXStmt::CoYieldStmt(e) => write!(f, "co_yield {};", e),
CXXStmt::CoAwaitStmt(e) => write!(f, "co_await {};", e),
CXXStmt::NullStmt => write!(f, ";"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CXXDecl {
Namespace {
name: String,
is_inline: bool,
members: Vec<CXXDecl>,
},
AnonymousNamespace {
members: Vec<CXXDecl>,
},
CXXRecord {
name: String,
kind: CXXRecordKind,
bases: Vec<BaseSpecifier>,
members: Vec<CXXMemberDecl>,
is_final: bool,
is_polymorphic: bool,
is_abstract: bool,
template_params: Option<Vec<TemplateParamDecl>>,
},
CXXRecordForward {
name: String,
kind: CXXRecordKind,
},
Function {
name: String,
return_ty: QualType,
params: Vec<CXXParamDecl>,
body: Option<CXXCompoundStmt>,
is_virtual: bool,
is_override: bool,
is_final: bool,
is_const: bool,
is_noexcept: bool,
is_constexpr: bool,
is_consteval: bool,
is_static: bool,
is_inline: bool,
is_explicit: bool,
is_deleted: bool,
is_defaulted: bool,
ref_qualifier: Option<RefQualifier>,
trailing_return_ty: Option<QualType>,
template_params: Option<Vec<TemplateParamDecl>>,
is_volatile: bool,
linkage: String,
init_list: Vec<CtorInit>,
},
ConstructorDecl {
class_name: String,
params: Vec<CXXParamDecl>,
body: Option<CXXCompoundStmt>,
init_list: Vec<CtorInit>,
is_explicit: bool,
is_constexpr: bool,
is_default: bool,
is_deleted: bool,
},
DestructorDecl {
class_name: String,
body: Option<CXXCompoundStmt>,
is_virtual: bool,
is_default: bool,
is_deleted: bool,
},
OperatorFunction {
op: CXXOperator,
return_ty: QualType,
params: Vec<CXXParamDecl>,
body: Option<CXXCompoundStmt>,
is_const: bool,
is_noexcept: bool,
is_constexpr: bool,
},
ConversionOperator {
target_ty: QualType,
is_explicit: bool,
is_const: bool,
is_noexcept: bool,
},
Variable {
name: String,
ty: QualType,
init: Option<Box<CXXExpr>>,
is_constexpr: bool,
is_constinit: bool,
is_static: bool,
is_thread_local: bool,
is_inline: bool,
},
Typedef {
name: String,
aliased_ty: QualType,
},
TypeAlias {
name: String,
aliased_ty: QualType,
},
UsingDeclaration {
name: CXXName,
},
UsingDirective {
namespace_name: NestedNameSpecifier,
},
UsingEnum {
enum_name: NestedNameSpecifier,
},
StaticAssert {
condition: String,
message: Option<String>,
},
FriendDeclaration(Box<CXXDecl>),
TemplateDeclaration {
params: Vec<TemplateParamDecl>,
decl: Box<CXXDecl>,
requires_clause: Option<Box<CXXExpr>>,
},
ExplicitInstantiation {
is_extern: Option<bool>,
name: CXXName,
},
ExplicitSpecialization {
decl: Box<CXXDecl>,
},
LinkageSpec {
language: String,
decls: Vec<CXXDecl>,
},
NamespaceAlias {
name: String,
aliased_ns: NestedNameSpecifier,
},
Enum {
name: String,
underlying_ty: Option<QualType>,
is_scoped: bool,
enumerators: Vec<EnumConstant>,
},
ConceptDecl {
name: String,
constraint_expr: Box<CXXExpr>,
},
Concept {
name: String,
constraint: Option<String>,
},
ModuleDecl {
name: String,
is_interface: bool,
is_partition: bool,
},
ModuleImport {
name: String,
is_exported: bool,
},
GlobalModuleFragment,
PrivateModuleFragment {
decls: Vec<CXXDecl>,
},
ExportDecl(Box<CXXDecl>),
}
impl fmt::Display for CXXDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CXXDecl::Namespace { name, .. } => write!(f, "namespace {}", name),
CXXDecl::AnonymousNamespace { .. } => write!(f, "namespace {{ ... }}"),
CXXDecl::CXXRecord { name, kind, .. } => write!(f, "{} {}", kind, name),
CXXDecl::CXXRecordForward { name, .. } => write!(f, "class {};", name),
CXXDecl::Function {
name, return_ty, ..
} => write!(f, "{} {}", return_ty, name),
CXXDecl::ConstructorDecl { class_name, .. } => write!(f, "{}()", class_name),
CXXDecl::DestructorDecl { class_name, .. } => write!(f, "~{}()", class_name),
CXXDecl::OperatorFunction { op, .. } => write!(f, "{}", op.canonical_name()),
CXXDecl::ConversionOperator { target_ty, .. } => write!(f, "operator {}", target_ty),
CXXDecl::Variable { name, ty, .. } => write!(f, "{} {}", ty, name),
CXXDecl::Typedef { name, .. } => write!(f, "typedef {}", name),
CXXDecl::TypeAlias { name, .. } => write!(f, "using {}", name),
CXXDecl::UsingDeclaration { name } => write!(f, "using {}", name),
CXXDecl::UsingDirective { namespace_name } => {
write!(f, "using namespace {}", namespace_name)
}
CXXDecl::UsingEnum { enum_name } => write!(f, "using enum {}", enum_name),
CXXDecl::StaticAssert { .. } => write!(f, "static_assert"),
CXXDecl::FriendDeclaration(_) => write!(f, "friend"),
CXXDecl::TemplateDeclaration { decl, .. } => write!(f, "template <...> {}", decl),
CXXDecl::ExplicitInstantiation { name, .. } => write!(f, "explicit template {}", name),
CXXDecl::ExplicitSpecialization { .. } => write!(f, "template<>"),
CXXDecl::LinkageSpec { language, .. } => write!(f, "extern \"{}\"", language),
CXXDecl::NamespaceAlias { name, .. } => write!(f, "namespace {} = ...", name),
CXXDecl::Enum { name, .. } => write!(f, "enum {}", name),
CXXDecl::ConceptDecl { name, .. } | CXXDecl::Concept { name, .. } => {
write!(f, "concept {}", name)
}
CXXDecl::ModuleDecl { name, .. } => write!(f, "module {}", name),
CXXDecl::ModuleImport { name, .. } => write!(f, "import {}", name),
CXXDecl::GlobalModuleFragment => write!(f, "module;"),
CXXDecl::PrivateModuleFragment { .. } => write!(f, "module :private;"),
CXXDecl::ExportDecl(d) => write!(f, "export {}", d),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CXXRecordKind {
Class,
Struct,
Union,
}
impl CXXRecordKind {
pub fn default_access(&self) -> AccessSpecifier {
match self {
CXXRecordKind::Class => AccessSpecifier::Private,
_ => AccessSpecifier::Public,
}
}
}
impl fmt::Display for CXXRecordKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CXXRecordKind::Class => write!(f, "class"),
CXXRecordKind::Struct => write!(f, "struct"),
CXXRecordKind::Union => write!(f, "union"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TemplateParamDecl {
TypeParm {
name: String,
is_typename: bool,
default_ty: Option<QualType>,
is_parameter_pack: bool,
},
NonTypeParm {
name: String,
ty: QualType,
default_value: Option<Box<CXXExpr>>,
is_parameter_pack: bool,
default_val: Option<String>,
},
TemplateTemplateParm {
name: String,
params: Vec<TemplateParamDecl>,
default_template: Option<CXXName>,
is_parameter_pack: bool,
},
}
impl fmt::Display for TemplateParamDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TemplateParamDecl::TypeParm {
name, is_typename, ..
} => {
if *is_typename {
write!(f, "typename {}", name)
} else {
write!(f, "class {}", name)
}
}
TemplateParamDecl::NonTypeParm { name, ty, .. } => write!(f, "{} {}", ty, name),
TemplateParamDecl::TemplateTemplateParm { name, .. } => {
write!(f, "template<...> class {}", name)
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct EnumConstant {
pub name: String,
pub value: Option<i64>,
}
impl fmt::Display for EnumConstant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(v) = self.value {
write!(f, "{} = {}", self.name, v)
} else {
write!(f, "{}", self.name)
}
}
}
#[derive(Debug, Clone)]
pub struct CXXTranslationUnit {
pub filename: String,
pub standard: CppStandard,
pub decls: Vec<CXXDecl>,
}
impl CXXTranslationUnit {
pub fn new(filename: &str, standard: CppStandard) -> Self {
Self {
filename: filename.to_string(),
standard,
decls: Vec::new(),
}
}
pub fn add_decl(&mut self, decl: CXXDecl) {
self.decls.push(decl);
}
}
impl fmt::Display for CXXTranslationUnit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "; Translation unit: {}", self.filename)?;
writeln!(f, "; Standard: {}", self.standard.as_flag())?;
for decl in &self.decls {
writeln!(f, "{}", decl)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CXXLambdaDecl {
pub captures: Vec<LambdaCapture>,
pub params: Vec<CXXParamDecl>,
pub body: Option<CXXCompoundStmt>,
pub return_ty: Option<QualType>,
pub is_mutable: bool,
pub is_constexpr: bool,
pub is_generic: bool,
pub closure_name: String,
}
impl CXXLambdaDecl {
pub fn new(closure_name: &str) -> Self {
Self {
captures: Vec::new(),
params: Vec::new(),
body: None,
return_ty: None,
is_mutable: false,
is_constexpr: false,
is_generic: false,
closure_name: closure_name.to_string(),
}
}
pub fn has_capture_default(&self) -> bool {
self.captures.iter().any(|c| {
matches!(
c,
LambdaCapture::DefaultByValue | LambdaCapture::DefaultByRef
)
})
}
pub fn has_explicit_captures(&self) -> bool {
self.captures
.iter()
.any(|c| matches!(c, LambdaCapture::Capture { .. }))
}
pub fn is_stateless(&self) -> bool {
!self.has_explicit_captures() && !self.has_capture_default()
}
}
impl fmt::Display for CXXLambdaDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[lambda:{}]", self.closure_name)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeTraitKind {
IsPod,
IsTrivial,
IsTriviallyCopyable,
IsStandardLayout,
IsLiteralType,
IsEmpty,
IsPolymorphic,
IsAbstract,
IsFinal,
IsAggregate,
IsUnsigned,
IsSigned,
IsEnum,
IsUnion,
IsClass,
HasVirtualDestructor,
HasUniqueObjectRepresentations,
IsConstructible,
IsTriviallyConstructible,
IsNothrowConstructible,
IsAssignable,
IsTriviallyAssignable,
IsNothrowAssignable,
IsDestructible,
IsTriviallyDestructible,
IsNothrowDestructible,
IsSwappable,
IsNothrowSwappable,
HasNothrowAssign,
HasNothrowCopy,
HasTrivialAssign,
HasTrivialCopy,
HasTrivialConstructor,
HasTrivialDestructor,
IsSame,
IsBaseOf,
IsConvertible,
IsNothrowConvertible,
IsLayoutCompatible,
IsPointerInterconvertibleBaseOf,
ArrayRank,
ArrayExtent,
RemoveCv,
RemoveReference,
RemovePointer,
AddPointer,
AddLvalueReference,
AddRvalueReference,
AddConst,
AddVolatile,
UnderlyingType,
Decay,
CommonType,
MakeSigned,
MakeUnsigned,
}
impl TypeTraitKind {
pub fn trait_name(&self) -> &'static str {
match self {
TypeTraitKind::IsPod => "__is_pod",
TypeTraitKind::IsTrivial => "__is_trivial",
TypeTraitKind::IsTriviallyCopyable => "__is_trivially_copyable",
TypeTraitKind::IsStandardLayout => "__is_standard_layout",
TypeTraitKind::IsLiteralType => "__is_literal_type",
TypeTraitKind::IsEmpty => "__is_empty",
TypeTraitKind::IsPolymorphic => "__is_polymorphic",
TypeTraitKind::IsAbstract => "__is_abstract",
TypeTraitKind::IsFinal => "__is_final",
TypeTraitKind::IsAggregate => "__is_aggregate",
TypeTraitKind::IsUnsigned => "__is_unsigned",
TypeTraitKind::IsSigned => "__is_signed",
TypeTraitKind::IsEnum => "__is_enum",
TypeTraitKind::IsUnion => "__is_union",
TypeTraitKind::IsClass => "__is_class",
TypeTraitKind::HasVirtualDestructor => "__has_virtual_destructor",
TypeTraitKind::HasUniqueObjectRepresentations => "__has_unique_object_representations",
TypeTraitKind::IsConstructible => "__is_constructible",
TypeTraitKind::IsTriviallyConstructible => "__is_trivially_constructible",
TypeTraitKind::IsNothrowConstructible => "__is_nothrow_constructible",
TypeTraitKind::IsAssignable => "__is_assignable",
TypeTraitKind::IsTriviallyAssignable => "__is_trivially_assignable",
TypeTraitKind::IsNothrowAssignable => "__is_nothrow_assignable",
TypeTraitKind::IsDestructible => "__is_destructible",
TypeTraitKind::IsTriviallyDestructible => "__is_trivially_destructible",
TypeTraitKind::IsNothrowDestructible => "__is_nothrow_destructible",
TypeTraitKind::IsSwappable => "__is_swappable",
TypeTraitKind::IsNothrowSwappable => "__is_nothrow_swappable",
TypeTraitKind::HasNothrowAssign => "__has_nothrow_assign",
TypeTraitKind::HasNothrowCopy => "__has_nothrow_copy",
TypeTraitKind::HasTrivialAssign => "__has_trivial_assign",
TypeTraitKind::HasTrivialCopy => "__has_trivial_copy",
TypeTraitKind::HasTrivialConstructor => "__has_trivial_constructor",
TypeTraitKind::HasTrivialDestructor => "__has_trivial_destructor",
TypeTraitKind::IsSame => "__is_same",
TypeTraitKind::IsBaseOf => "__is_base_of",
TypeTraitKind::IsConvertible => "__is_convertible",
TypeTraitKind::IsNothrowConvertible => "__is_nothrow_convertible",
TypeTraitKind::IsLayoutCompatible => "__is_layout_compatible",
TypeTraitKind::IsPointerInterconvertibleBaseOf => {
"__is_pointer_interconvertible_base_of"
}
TypeTraitKind::ArrayRank => "__array_rank",
TypeTraitKind::ArrayExtent => "__array_extent",
TypeTraitKind::RemoveCv => "__remove_cv",
TypeTraitKind::RemoveReference => "__remove_reference",
TypeTraitKind::RemovePointer => "__remove_pointer",
TypeTraitKind::AddPointer => "__add_pointer",
TypeTraitKind::AddLvalueReference => "__add_lvalue_reference",
TypeTraitKind::AddRvalueReference => "__add_rvalue_reference",
TypeTraitKind::AddConst => "__add_const",
TypeTraitKind::AddVolatile => "__add_volatile",
TypeTraitKind::UnderlyingType => "__underlying_type",
TypeTraitKind::Decay => "__decay",
TypeTraitKind::CommonType => "__common_type",
TypeTraitKind::MakeSigned => "__make_signed",
TypeTraitKind::MakeUnsigned => "__make_unsigned",
}
}
pub fn is_variadic(&self) -> bool {
matches!(
self,
TypeTraitKind::IsConstructible
| TypeTraitKind::IsTriviallyConstructible
| TypeTraitKind::IsNothrowConstructible
)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CXXMethodDecl {
pub name: String,
pub parent_class: String,
pub return_ty: QualType,
pub params: Vec<CXXParamDecl>,
pub body: Option<CXXCompoundStmt>,
pub is_virtual: bool,
pub is_pure_virtual: bool,
pub is_override: bool,
pub is_final: bool,
pub is_const: bool,
pub is_volatile: bool,
pub is_static: bool,
pub is_noexcept: bool,
pub is_constexpr: bool,
pub is_consteval: bool,
pub is_explicit: bool,
pub ref_qualifier: Option<RefQualifier>,
pub trailing_return_ty: Option<QualType>,
pub access: AccessSpecifier,
pub template_params: Option<Vec<TemplateParamDecl>>,
}
impl CXXMethodDecl {
pub fn new(name: &str, parent_class: &str, return_ty: QualType) -> Self {
Self {
name: name.to_string(),
parent_class: parent_class.to_string(),
return_ty,
params: Vec::new(),
body: None,
is_virtual: false,
is_pure_virtual: false,
is_override: false,
is_final: false,
is_const: false,
is_volatile: false,
is_static: false,
is_noexcept: false,
is_constexpr: false,
is_consteval: false,
is_explicit: false,
ref_qualifier: None,
trailing_return_ty: None,
access: AccessSpecifier::Public,
template_params: None,
}
}
pub fn qualified_name(&self) -> String {
format!("{}::{}", self.parent_class, self.name)
}
}
impl fmt::Display for CXXMethodDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_virtual {
write!(f, "virtual ")?;
}
if self.is_static {
write!(f, "static ")?;
}
if self.is_constexpr {
write!(f, "constexpr ")?;
}
write!(
f,
"{} {}::{}()",
self.return_ty, self.parent_class, self.name
)?;
if self.is_const {
write!(f, " const")?;
}
if self.is_override {
write!(f, " override")?;
}
if self.is_final {
write!(f, " final")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CXXConversionDecl {
pub target_ty: QualType,
pub is_explicit: bool,
pub is_const: bool,
pub is_noexcept: bool,
pub body: Option<CXXCompoundStmt>,
}
impl CXXConversionDecl {
pub fn new(target_ty: QualType) -> Self {
Self {
target_ty,
is_explicit: false,
is_const: false,
is_noexcept: false,
body: None,
}
}
pub fn canonical_name(&self) -> String {
format!("operator {}", self.target_ty)
}
}
impl fmt::Display for CXXConversionDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_explicit {
write!(f, "explicit ")?;
}
write!(f, "operator {}", self.target_ty)?;
if self.is_const {
write!(f, " const")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CoroutineSuspend {
Initial,
Final,
SuspendAlways,
SuspendNever,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CoroutineStmt {
pub kind: CoroutineSuspend,
pub operand: Option<Box<CXXExpr>>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct StructuredBindingDecl {
pub names: Vec<String>,
pub initializer: Box<CXXExpr>,
pub is_ref: bool,
}
impl fmt::Display for StructuredBindingDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "auto [")?;
for (i, name) in self.names.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", name)?;
}
write!(f, "] = {}", self.initializer)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ConceptDefinition {
pub name: String,
pub params: Vec<TemplateParamDecl>,
pub constraint_expr: Box<CXXExpr>,
}
impl fmt::Display for ConceptDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "concept {} = {}", self.name, self.constraint_expr)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn int_ty() -> QualType {
QualType::new(TypeNode::Int)
}
#[test]
fn test_cxx_record_kind_default_access() {
assert_eq!(
CXXRecordKind::Class.default_access(),
AccessSpecifier::Private
);
assert_eq!(
CXXRecordKind::Struct.default_access(),
AccessSpecifier::Public
);
assert_eq!(
CXXRecordKind::Union.default_access(),
AccessSpecifier::Public
);
}
#[test]
fn test_template_param_display() {
let tp = TemplateParamDecl::TypeParm {
name: "T".into(),
is_typename: true,
default_ty: None,
is_parameter_pack: false,
};
assert_eq!(format!("{}", tp), "typename T");
}
#[test]
fn test_cxx_operator_canonical() {
assert_eq!(CXXOperator::Add.canonical_name(), "operator+");
assert_eq!(CXXOperator::Call.canonical_name(), "operator()");
assert_eq!(CXXOperator::Spaceship.canonical_name(), "operator<=>");
}
#[test]
fn test_cxx_expr_display_basic() {
assert_eq!(format!("{}", CXXExpr::ThisExpr), "this");
assert_eq!(format!("{}", CXXExpr::NullPtrLiteral), "nullptr");
assert_eq!(format!("{}", CXXExpr::BoolLiteral(true)), "true");
}
#[test]
fn test_base_specifier() {
let b = BaseSpecifier::public("Base");
assert_eq!(format!("{}", b), "public Base");
assert!(!b.is_virtual);
}
#[test]
fn test_nested_name_specifier() {
let mut ns = NestedNameSpecifier::new();
ns.push_namespace("std".into());
ns.push_type("vector".into());
assert_eq!(format!("{}", ns), "std::vector");
}
#[test]
fn test_cxx_translation_unit() {
let mut tu = CXXTranslationUnit::new("test.cpp", CppStandard::Cxx17);
tu.add_decl(CXXDecl::CXXRecordForward {
name: "Foo".into(),
kind: CXXRecordKind::Class,
});
assert_eq!(tu.decls.len(), 1);
}
#[test]
fn test_lambda_capture_display() {
assert_eq!(format!("{}", LambdaCapture::DefaultByValue), "=");
assert_eq!(format!("{}", LambdaCapture::DefaultByRef), "&");
assert_eq!(format!("{}", LambdaCapture::ThisByRef), "this");
}
#[test]
fn test_ref_qualifier_display() {
assert_eq!(format!("{}", RefQualifier::LValue), "&");
assert_eq!(format!("{}", RefQualifier::RValue), "&&");
}
#[test]
fn test_cxx_param_decl_default() {
let p = CXXParamDecl::new("x", QualType::int());
assert_eq!(p.name, "x");
assert!(!p.has_default);
}
#[test]
fn test_cxx_lambda_decl() {
let l = CXXLambdaDecl::new("lambda_0");
assert!(l.is_stateless());
assert!(!l.has_capture_default());
assert!(!l.has_explicit_captures());
}
#[test]
fn test_cxx_lambda_decl_with_capture() {
let mut l = CXXLambdaDecl::new("lambda_1");
l.captures.push(LambdaCapture::Capture {
name: "x".into(),
by_ref: false,
capture_default: None,
init: None,
});
assert!(!l.is_stateless());
assert!(l.has_explicit_captures());
}
#[test]
fn test_cxx_method_decl() {
let m = CXXMethodDecl::new("foo", "MyClass", QualType::int());
assert_eq!(m.name, "foo");
assert_eq!(m.qualified_name(), "MyClass::foo");
}
#[test]
fn test_cxx_conversion_decl() {
let c = CXXConversionDecl::new(QualType::int());
assert_eq!(c.canonical_name(), "operator int");
}
#[test]
fn test_type_trait_kind_names() {
assert_eq!(TypeTraitKind::IsPod.trait_name(), "__is_pod");
assert_eq!(TypeTraitKind::IsTrivial.trait_name(), "__is_trivial");
}
#[test]
fn test_type_trait_variadic() {
assert!(TypeTraitKind::IsConstructible.is_variadic());
assert!(!TypeTraitKind::IsPod.is_variadic());
}
#[test]
fn test_new_cxx_expr_types() {
assert!(format!("{}", CXXExpr::FloatLiteral(3.14)).contains("3.14"));
assert!(format!("{}", CXXExpr::StringLiteral("hi".into())).contains("hi"));
}
#[test]
fn test_structured_binding_display() {
let sb = StructuredBindingDecl {
names: vec!["x".into(), "y".into()],
initializer: Box::new(CXXExpr::IntegerLiteral(42)),
is_ref: false,
};
assert!(format!("{}", sb).contains("auto ["));
assert!(format!("{}", sb).contains("x, y"));
}
}