-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
base: main
Are you sure you want to change the base?
Conversation
Right now the tests aren't very useful, they're just testing syn's ability to roundtrip tokens. If you can add functionality to print |
Now that I think about it, you're right the tests aren't very useful. For the conversion to C part I want to keep that separate from the extraction logic, so it'll belong to a different struct. I'll add support for extracting all the other types that we need tomorrow, remove the tests for extraction and add a simple stub for a rust to C converter and try to test that. My intial reasoning for the tests was because locally I wasn't really sure if the visitor was visiting every item regardless of whether it was inside a function, top level etc. This could mean that some symbols would accidentally be missed when extracting and it wouldn't be easy to figure out which ones. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some review for ir
, I still need to take a look at translation
.
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 | ||
} |
There was a problem hiding this comment.
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
)
There was a problem hiding this comment.
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.
pub enum SkipItem { | ||
Constant(Predicate<Constant>), | ||
Function(Predicate<Function>), | ||
Static(Predicate<Static>), | ||
TypeAlias(Predicate<TypeAlias>), | ||
Struct(Predicate<Struct>), | ||
Field(Predicate<Field>), | ||
Union(Predicate<Union>), | ||
} |
There was a problem hiding this comment.
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.
pub fn add(left: usize, right: usize) -> usize { | ||
left + right | ||
} | ||
#![allow(dead_code)] |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
/// A `SymbolTable` represents a collected set of top-level Rust items | ||
/// relevant to FFI generation or analysis, including foreign functions/statics, | ||
/// type aliases, structs, unions, and constants. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two small nits:
- Docs don't need to restate the type, rustdoc handles the association for you
- The first paragraph needs to be 1-2 lines max since it gets used as a summary
So something like this:
/// Represents a collected set of top-level Rust items relevant to FFI generation or analysis.
///
/// Includes foreign functions/statics, type aliases, structs, unions, and constants.
pub fn contains_struct(&self, ident: &str) -> bool { | ||
self.structs() | ||
.iter() | ||
.any(|structure| structure.ident().to_string() == ident) | ||
} | ||
|
||
pub fn contains_union(&self, ident: &str) -> bool { | ||
self.unions() | ||
.iter() | ||
.any(|structure| structure.ident().to_string() == ident) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.ident()
without .to_string()
should be fine here, Ident
implements PartialEq
for everything AsRef<str>
https://docs.rs/proc-macro2/latest/proc_macro2/struct.Ident.html#impl-PartialEq%3CT%3E-for-Ident
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; |
There was a problem hiding this comment.
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.
/// relevant to FFI generation or analysis, including foreign functions/statics, | ||
/// type aliases, structs, unions, and constants. | ||
#[derive(Debug)] | ||
pub struct SymbolTable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have any ideas for a different name than SymbolTable
? A "symbol table" is typically a list of the exported symbols from a library which doesn't really apply here.
Maybe something like ItemTable
or MappableItems
(mappable from C->Rust)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about FfiItems
?
} | ||
} | ||
|
||
fn collect_fields(fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>) -> Vec<Field> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syn::punctuated::Punctuated
is a long path, you can just import it
if ty == "&str" { | ||
return "char*".to_string(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't correct, &str
isn't a C-safe type
Ok(s) | ||
} | ||
|
||
pub fn translate_primitive_type(&self, ty: &str) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These functions should be taking syn
types, syn's &str
roundtrip isn't reliable. I can help with that in a follow up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
translate_primitive_type
would have to remain &str
since syn
only supports Path
representation once all other parts of a type have been stripped from it. The other functions and those in the future will take in syn
types.
To scope this a bit, I think it would be good for you to split this into 3-4 separate PRs:
This would build up a test structure like this:
Which gets added with the steps:
At some point the actual test functions should move to |
I'm a little confused on the last part, do you mean that Then in the translation PR And then the same thing for the skip PR? |
This isn't anything concrete, but that is what I had in mind. It would be possible to always use
You can feel free to play with the ideas here a bit, I'm just modeling something after how we do our bless tests in Rust CI. |
Could you elaborate on the LIBC_BLESS=1 part? When the tests run it would assert that the expansion matches, if it fails to assert it checks if that environment variable is set and if it is it modifies .c with what the expansion actually gives? |
That's correct! Similar to how we use RUSTC_BLESS or |
Description
This PR adds a helper function for calling cargo expand, as well as the start of constructing a
SymbolTable
that extracts all required items from the codebase (for now only type aliases).It also has two tests, although more are to be added.
It also adds new dependencies:
TokenStream
in struct fields.Sources
N/A
Checklist
libc-test/semver
have been updated*LAST
or*MAX
areincluded (see #3131)
cd libc-test && cargo test --target mytarget
);especially relevant for platforms that may not be checked in CI