Skip to content

ctest: Add type alias extraction logic #4477

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ctest-next/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ publish = false

[dependencies]
cc = "1.2.25"
proc-macro2 = "1.0.95"
quote = "1.0.40"
syn = { version = "2.0.101", features = ["full", "visit", "visit-mut", "fold"] }
16 changes: 16 additions & 0 deletions ctest-next/src/cargo_expand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::{env, error::Error, path::Path, process::Command};

/// Use cargo expand to expand all macros and pretty print the crate
/// into a single file.
pub fn expand(crate_path: &Path) -> Result<String, Box<dyn Error>> {
let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));

let output = Command::new(cargo)
.arg("expand")
.current_dir(crate_path)
.output()?;

let expanded = std::str::from_utf8(&output.stdout)?.to_string();

Ok(expanded)
}
37 changes: 37 additions & 0 deletions ctest-next/src/ir/constant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use proc_macro2::TokenStream;
use syn::Ident;

#[derive(Debug)]
pub struct Constant {
public: bool,
ident: Ident,
ty: TokenStream,
value: TokenStream,
}

impl Constant {
pub fn new(public: bool, ident: Ident, ty: TokenStream, value: TokenStream) -> Self {
Self {
public,
ident,
ty,
value,
}
}

pub fn public(&self) -> bool {
self.public
}

pub fn ident(&self) -> &Ident {
&self.ident
}

pub fn ty(&self) -> &TokenStream {
&self.ty
}

pub fn value(&self) -> &TokenStream {
&self.value
}
Comment on lines +22 to +36
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For our public API, we probably don't want to expose syn/pm2 types to give us more flexibility in the future. So for now you can change any -> TokenStream methods to pub(crate) and mark them #[expect(unused)] if needed. and then ident should return a String. Or also save an ident_string field so we can return an &str.

(applies to all the types in ir)

}
27 changes: 27 additions & 0 deletions ctest-next/src/ir/field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use proc_macro2::TokenStream;
use syn::Ident;

#[derive(Debug)]
pub struct Field {
public: bool,
ident: Option<Ident>,
ty: TokenStream,
}

impl Field {
pub fn new(public: bool, ident: Option<Ident>, ty: TokenStream) -> Self {
Self { public, ident, ty }
}

pub fn public(&self) -> bool {
self.public
}

pub fn ident(&self) -> &Option<Ident> {
&self.ident
}

pub fn ty(&self) -> &TokenStream {
&self.ty
}
}
44 changes: 44 additions & 0 deletions ctest-next/src/ir/function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use proc_macro2::TokenStream;
use syn::Ident;

use crate::ir::Parameter;

#[derive(Debug)]
pub struct Function {
public: bool,
ident: Ident,
parameters: Vec<Parameter>,
return_value: TokenStream,
}

impl Function {
pub fn new(
public: bool,
ident: Ident,
parameters: Vec<Parameter>,
return_value: TokenStream,
) -> Self {
Self {
public,
ident,
parameters,
return_value,
}
}

pub fn public(&self) -> bool {
self.public
}

pub fn ident(&self) -> &Ident {
&self.ident
}

pub fn parameters(&self) -> &Vec<Parameter> {
&self.parameters
}

pub fn return_value(&self) -> &TokenStream {
&self.return_value
}
}
17 changes: 17 additions & 0 deletions ctest-next/src/ir/mod.rs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a nit, this is more of an ast than ir - ast is just structure, ir typically has more information associated with it.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
mod constant;
mod field;
mod function;
mod parameter;
mod static_variable;
mod structure;
mod type_alias;
mod union;

pub use constant::Constant;
pub use field::Field;
pub use function::Function;
pub use parameter::Parameter;
pub use static_variable::Static;
pub use structure::Struct;
pub use type_alias::TypeAlias;
pub use union::Union;
Comment on lines +10 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you keep these names consistent with syn's https://docs.rs/syn/latest/syn/enum.Item.html which are consistent with rustc? So Constant->Const, Function -> Fn, TypeAlias -> Type.

I actually prefer the names you have now, but consistency with the ecosystem is useful.

21 changes: 21 additions & 0 deletions ctest-next/src/ir/parameter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use proc_macro2::TokenStream;

#[derive(Debug)]
pub struct Parameter {
pattern: TokenStream,
ty: TokenStream,
}

impl Parameter {
pub fn new(pattern: TokenStream, ty: TokenStream) -> Self {
Self { pattern, ty }
}

pub fn pattern(&self) -> &TokenStream {
&self.pattern
}

pub fn ty(&self) -> &TokenStream {
&self.ty
}
}
28 changes: 28 additions & 0 deletions ctest-next/src/ir/static_variable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use proc_macro2::TokenStream;
use syn::Ident;

#[derive(Debug)]
pub struct Static {
public: bool,
ident: Ident,
ty: TokenStream,
// We do not care about the value as we only parse foreign statics.
}

impl Static {
pub fn new(public: bool, ident: Ident, ty: TokenStream) -> Self {
Self { public, ident, ty }
}

pub fn public(&self) -> bool {
self.public
}

pub fn ident(&self) -> &Ident {
&self.ident
}

pub fn ty(&self) -> &TokenStream {
&self.ty
}
}
32 changes: 32 additions & 0 deletions ctest-next/src/ir/structure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use syn::Ident;

use crate::ir::Field;

#[derive(Debug)]
pub struct Struct {
public: bool,
ident: Ident,
fields: Vec<Field>,
}

impl Struct {
pub fn new(public: bool, ident: Ident, fields: Vec<Field>) -> Self {
Self {
public,
ident,
fields,
}
}

pub fn public(&self) -> bool {
self.public
}

pub fn ident(&self) -> &Ident {
&self.ident
}

pub fn fields(&self) -> &Vec<Field> {
&self.fields
}
}
27 changes: 27 additions & 0 deletions ctest-next/src/ir/type_alias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use proc_macro2::TokenStream;
use syn::Ident;

#[derive(Debug)]
pub struct TypeAlias {
public: bool,
ident: Ident,
ty: TokenStream,
}

impl TypeAlias {
pub fn new(public: bool, ident: Ident, ty: TokenStream) -> Self {
Self { public, ident, ty }
}

pub fn public(&self) -> bool {
self.public
}

pub fn ident(&self) -> &Ident {
&self.ident
}

pub fn ty(&self) -> &TokenStream {
&self.ty
}
}
32 changes: 32 additions & 0 deletions ctest-next/src/ir/union.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use syn::Ident;

use crate::ir::Field;

#[derive(Debug)]
pub struct Union {
public: bool,
ident: Ident,
fields: Vec<Field>,
}

impl Union {
pub fn new(public: bool, ident: Ident, fields: Vec<Field>) -> Self {
Self {
public,
ident,
fields,
}
}

pub fn public(&self) -> bool {
self.public
}

pub fn ident(&self) -> &Ident {
&self.ident
}

pub fn fields(&self) -> &Vec<Field> {
&self.fields
}
}
20 changes: 8 additions & 12 deletions ctest-next/src/lib.rs
Copy link
Contributor

@tgross35 tgross35 Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add #![warn(unreachable_pub)] and #![warn(missing_docs)]? This will make it a bit more obvious what API is user public vs. crate public. Even a small docstring is helpful for public items so we don't forget, that can be expanded later.

Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#![allow(dead_code)]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need to be removed before merge. Add #[expect(dead_code)] to specific types/functions instead.

#[cfg(test)]
mod tests {
use super::*;
mod cargo_expand;
mod ir;
mod skip;
mod symbol_table;
mod translation;

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
// TODO: Implement proper error types instead of Box<dyn Error>.
// TODO: Add documentation.
39 changes: 39 additions & 0 deletions ctest-next/src/skip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::ir::{Constant, Field, Function, Static, Struct, TypeAlias, Union};

/// A boxed predicate function wrapper for filtering items of type `T`.
pub struct Predicate<T>(Box<dyn Fn(T) -> bool>);

impl<T> Predicate<T> {
pub fn new<F>(f: F) -> Self
where
F: Fn(T) -> bool + 'static,
{
Self(Box::new(f))
}

pub fn test(&self, value: T) -> bool {
(self.0)(value)
}
}

impl<T, F> From<F> for Predicate<T>
where
F: Fn(T) -> bool + 'static,
{
fn from(f: F) -> Self {
Predicate(Box::new(f))
}
}

/// Specifies a filter condition for skipping specific kinds of items.
///
/// To be used as `SkipItem::Struct((|s: Struct| !s.public()).into())`
pub enum SkipItem {
Constant(Predicate<Constant>),
Function(Predicate<Function>),
Static(Predicate<Static>),
TypeAlias(Predicate<TypeAlias>),
Struct(Predicate<Struct>),
Field(Predicate<Field>),
Union(Predicate<Union>),
}
Comment on lines +31 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be reversed; in AST add a pub enum Item { Constant(Constant), ... } and then Predicate can wrap that.

Loading
Loading