Skip to content

Misc improvements to codegen and testing #9395

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

Merged
merged 11 commits into from
Mar 27, 2025
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ function(rerun_strict_warning_settings target)
# TODO(andreas): Try to enable /Wall
target_compile_options(${target} PRIVATE /W4)

target_compile_options(${target} PRIVATE /we4996) # Using deprecated functions is an error

if(BUILD_SHARED_LIBS)
# If we are building as shared libs, we are going to have to disable the C4251
# warning, as it would trigger for any datatype derived from a STL class
Expand All @@ -51,6 +53,7 @@ function(rerun_strict_warning_settings target)
-Wall
-Wcast-align
-Wcast-qual
-Werror=deprecated-declarations
-Wextra
-Wformat=2
-Wmissing-include-dirs
Expand Down
30 changes: 22 additions & 8 deletions crates/build/re_types_builder/src/codegen/cpp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,10 +519,17 @@ impl QuotedObject {
));
let constant_name = archetype_component_descriptor_constant_ident(obj_field);
let field_type = obj_field.typ.fqname();
let field_type = quote_fqname_as_type_path(
&mut hpp_includes,
field_type.expect("Component field must have a non trivial type"),
);

let field_type = if let Some(field_type) = field_type {
quote_fqname_as_type_path(&mut hpp_includes, field_type)
} else {
reporter.error(
&obj_field.virtpath,
&obj_field.fqname,
"Component field must have a non trivial type",
);
TokenStream::new()
};
quote! {
#NEWLINE_TOKEN
#comment
Expand Down Expand Up @@ -559,10 +566,17 @@ impl QuotedObject {
let field_assignments = obj.fields.iter().map(|obj_field| {
let field_ident = field_name_ident(obj_field);
let field_type = obj_field.typ.fqname();
let field_type = quote_fqname_as_type_path(
&mut hpp_includes,
field_type.expect("Component field must have a non trivial type"),
);
let field_type =
if let Some(field_type) = field_type {
quote_fqname_as_type_path(&mut hpp_includes, field_type)
} else {
reporter.error(
&obj_field.virtpath,
&obj_field.fqname,
"Component field must have a non trivial type",
);
TokenStream::new()
};
let descriptor = archetype_component_descriptor_constant_ident(obj_field);
quote! {
archetype.#field_ident = ComponentBatch::empty<#field_type>(#descriptor).value_or_throw();
Expand Down
30 changes: 25 additions & 5 deletions crates/build/re_types_builder/src/codegen/python/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ const FIELD_CONVERTER_SUFFIX: &str = "__field_converter_override";

// ---

fn classmethod_decorators(obj: &Object) -> String {
// We need to decorate all class methods as deprecated
if let Some(deprecation_notice) = obj.deprecation_notice() {
format!(r#"@deprecated("""{deprecation_notice}""")"#)
} else {
Default::default()
}
}

// ---

/// Python-specific helpers for [`Object`].
trait PythonObjectExt {
/// Returns `true` if the object is a delegating component.
Expand Down Expand Up @@ -909,7 +920,9 @@ fn code_for_enum(
code.push_indented(
1,
format!(
r#"@classmethod
r#"
@classmethod
{extra_decorators}
def auto(cls, val: str | int | {enum_name}) -> {enum_name}:
'''Best-effort converter, including a case-insensitive string matcher.'''
if isinstance(val, {enum_name}):
Expand All @@ -924,7 +937,8 @@ def auto(cls, val: str | int | {enum_name}) -> {enum_name}:
if variant.name.lower() == val_lower:
return variant
raise ValueError(f"Cannot convert {{val}} to {{cls.__name__}}")
"#
"#,
extra_decorators = classmethod_decorators(obj)
),
1,
);
Expand Down Expand Up @@ -2482,12 +2496,14 @@ fn quote_clear_methods(obj: &Object) -> String {
)

@classmethod
{extra_decorators}
def _clear(cls) -> {classname}:
"""Produce an empty {classname}, bypassing `__init__`."""
inst = cls.__new__(cls)
inst.__attrs_clear__()
return inst
"#
"#,
extra_decorators = classmethod_decorators(obj)
))
}

Expand Down Expand Up @@ -2538,6 +2554,7 @@ fn quote_partial_update_methods(reporter: &Reporter, obj: &Object, objects: &Obj
unindent(&format!(
r#"
@classmethod
{extra_decorators}
def from_fields(
cls,
*,
Expand All @@ -2564,7 +2581,8 @@ fn quote_partial_update_methods(reporter: &Reporter, obj: &Object, objects: &Obj
def cleared(cls) -> {name}:
"""Clear all the fields of a `{name}`."""
return cls.from_fields(clear_unset=True)
"#
"#,
extra_decorators = classmethod_decorators(obj)
))
}

Expand Down Expand Up @@ -2625,6 +2643,7 @@ fn quote_columnar_methods(reporter: &Reporter, obj: &Object, objects: &Objects)
unindent(&format!(
r#"
@classmethod
{extra_decorators}
def columns(
cls,
*,
Expand Down Expand Up @@ -2672,7 +2691,8 @@ fn quote_columnar_methods(reporter: &Reporter, obj: &Object, objects: &Objects)

indicator_column = cls.indicator().partition(np.zeros(len(sizes)))
return ComponentColumnList([indicator_column] + columns)
"#
"#,
extra_decorators = classmethod_decorators(obj)
))
}

Expand Down
15 changes: 10 additions & 5 deletions crates/build/re_types_builder/src/codegen/rust/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ fn generate_object_file(
code.push_str("#![allow(clippy::too_many_arguments)]\n"); // e.g. `AffixFuzzer1::new`
code.push_str("#![allow(clippy::too_many_lines)]\n");
if obj.deprecation_notice().is_some() {
code.push_str("#![allow(deprecated)]\n");
code.push_str("#![expect(deprecated)]\n");
}

if obj.is_enum() {
Expand Down Expand Up @@ -248,7 +248,7 @@ fn generate_mod_file(
let type_name = &obj.name;

if obj.deprecation_notice().is_some() {
code.push_str("#[allow(deprecated)]\n");
code.push_str("#[expect(deprecated)]\n");
}

code.push_str(&format!("pub use self::{module_name}::{type_name};\n"));
Expand Down Expand Up @@ -851,7 +851,7 @@ fn quote_trait_impls_from_obj(
quote_trait_impls_for_datatype_or_component(objects, arrow_registry, obj)
}

ObjectKind::Archetype => quote_trait_impls_for_archetype(obj),
ObjectKind::Archetype => quote_trait_impls_for_archetype(reporter, obj),

ObjectKind::View => quote_trait_impls_for_view(reporter, obj),
}
Expand Down Expand Up @@ -1051,7 +1051,7 @@ fn quote_trait_impls_for_datatype_or_component(
}
}

fn quote_trait_impls_for_archetype(obj: &Object) -> TokenStream {
fn quote_trait_impls_for_archetype(reporter: &Reporter, obj: &Object) -> TokenStream {
#![allow(clippy::collapsible_else_if)]

let Object {
Expand Down Expand Up @@ -1094,7 +1094,12 @@ fn quote_trait_impls_for_archetype(obj: &Object) -> TokenStream {
.iter()
.map(|field| {
let Some(component_name) = field.typ.fqname() else {
panic!("Archetype field must be an object/union or an array/vector of such")
reporter.error(
&obj.virtpath,
&obj.fqname,
"Archetype field must be an object/union or an array/vector of such",
);
return TokenStream::new();
};

let archetype_name = &obj.fqname;
Expand Down
8 changes: 6 additions & 2 deletions crates/build/re_types_builder/src/codegen/rust/reflection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ fn generate_component_reflection(
#[doc = "Generates reflection about all known components."]
#[doc = ""]
#[doc = "Call only once and reuse the results."]
#[allow(deprecated)]
fn generate_component_reflection() -> Result<ComponentReflectionMap, SerializationError> {
re_tracing::profile_function!();
let array = [
Expand All @@ -187,7 +186,12 @@ fn generate_archetype_reflection(reporter: &Reporter, objects: &Objects) -> Toke
{
let quoted_field_reflections = obj.fields.iter().map(|field| {
let Some(component_name) = field.typ.fqname() else {
panic!("archetype field must be an object/union or an array/vector of such")
reporter.error(
&field.virtpath,
&field.fqname,
"Archetype field must be an object/union or an array/vector of such",
);
return TokenStream::new();
};
let name = &field.name;
let display_name = re_case::to_human_case(&field.name);
Expand Down
7 changes: 7 additions & 0 deletions crates/build/re_types_builder/src/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,13 @@ mod doclink_translation {

scope = object.scope().unwrap_or_default();
is_unreleased = object.is_attr_set(crate::ATTR_DOCS_UNRELEASED);

if let Some(deprecation_notice) = object.deprecation_notice() {
return Err(format!(
"Found doclink to deprecated object '{}': {deprecation_notice}",
object.fqname,
));
}
}

Ok(match target {
Expand Down
72 changes: 47 additions & 25 deletions crates/build/re_types_builder/src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1038,31 +1038,8 @@ impl Type {
}
}

let is_int = matches!(
typ,
FbsBaseType::Byte
| FbsBaseType::UByte
| FbsBaseType::Short
| FbsBaseType::UShort
| FbsBaseType::Int
| FbsBaseType::UInt
| FbsBaseType::Long
| FbsBaseType::ULong
);
if is_int {
// Hack needed because enums get `typ == FbsBaseType::Byte`,
// or whatever integer type the enum was assigned to.
let enum_index = field_type.index() as usize;
if enum_index < enums.len() {
// It is an enum.
assert!(
typ == FbsBaseType::UByte,
"{virtpath}: For consistency, enums must be declared as the `ubyte` type"
);

let enum_ = &enums[field_type.index() as usize];
return Self::Object(enum_.name().to_owned());
}
if let Some(enum_fqname) = try_get_enum_fqname(enums, field_type, typ, virtpath) {
return Self::Object(enum_fqname);
}

match typ {
Expand Down Expand Up @@ -1099,6 +1076,7 @@ impl Type {
field_type,
field_type.element(),
attrs,
virtpath,
),
length: field_type.fixed_length() as usize,
},
Expand All @@ -1109,6 +1087,7 @@ impl Type {
field_type,
field_type.element(),
attrs,
virtpath,
),
},
FbsBaseType::UType | FbsBaseType::Vector64 => {
Expand Down Expand Up @@ -1264,6 +1243,44 @@ impl Type {
}
}

fn try_get_enum_fqname(
enums: &[FbsEnum<'_>],
field_type: FbsType<'_>,
typ: FbsBaseType,
virtpath: &str,
) -> Option<String> {
if is_int(typ) {
// Hack needed because enums get `typ == FbsBaseType::Byte`,
// or whatever integer type the enum was assigned to.
let enum_index = field_type.index() as usize;
if enum_index < enums.len() {
// It is an enum.
assert!(
typ == FbsBaseType::UByte,
"{virtpath}: For consistency, enums must be declared as the `ubyte` type"
);

let enum_ = &enums[field_type.index() as usize];
return Some(enum_.name().to_owned());
}
}
None
}

fn is_int(typ: FbsBaseType) -> bool {
matches!(
typ,
FbsBaseType::Byte
| FbsBaseType::UByte
| FbsBaseType::Short
| FbsBaseType::UShort
| FbsBaseType::Int
| FbsBaseType::UInt
| FbsBaseType::Long
| FbsBaseType::ULong
)
}

/// The underlying element type for arrays/vectors/maps.
///
/// Flatbuffers doesn't support directly nesting multiple layers of arrays, they
Expand Down Expand Up @@ -1293,7 +1310,12 @@ impl ElementType {
outer_type: FbsType<'_>,
inner_type: FbsBaseType,
attrs: &Attributes,
virtpath: &str,
) -> Self {
if let Some(enum_fqname) = try_get_enum_fqname(enums, outer_type, inner_type, virtpath) {
return Self::Object(enum_fqname);
}

// TODO(jleibs): Clean up fqname plumbing
let fqname = "???";
if let Some(type_override) = attrs.try_get::<String>(fqname, ATTR_RERUN_OVERRIDE_TYPE) {
Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_chunk/src/batcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ impl ChunkBatcherConfig {
}

// Deprecated
#[allow(deprecated)]
#[expect(deprecated)]
if let Ok(s) = std::env::var(Self::ENV_MAX_CHUNK_ROWS_IF_UNSORTED) {
new.chunk_max_rows_if_unsorted =
s.parse().map_err(|err| ChunkBatcherError::ParseConfig {
Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_types/src/reflection/mod.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading