Skip to content

fix(ModelIO): repair corrupted model files and harden model load/save - #1369

Merged
BenPinet merged 16 commits into
nextfrom
fix/model-io-robustness
Oct 21, 2026
Merged

BenPinet merged 16 commits into
nextfrom
fix/model-io-robustness

Conversation

@BotellaA

@BotellaA BotellaA commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Robustness fixes for native BRep and Section IO. Corrupted or incomplete model files are now repaired with a warning wherever a sensible repair exists. When none exists, loading throws an OpenGeodeException instead of a bare std exception, out of bounds reads or silently inconsistent data. Saving checks its output streams, RegularGrid component meshes can be saved, and the Logger level is restored when a load fails. The serialized format is unchanged, so no new backward IO files are needed. The new test-model-corrupted-files builds every corrupted file from a saved model: it patches, removes or swaps files inside the archive. Each test fails before its fix and passes after it.

  • fix(Relationships): rebuild component index from vertex ids on load: the uuid -> graph vertex map was read from the file and trusted. With an inconsistent map, a component got another component's relations. An out of range index was read out of bounds in UuidToIndex::update (its NO_ID check is debug-only), and erase(index_t) scanned the whole map for each isolated vertex. The map is still read, for format compatibility, but it is now rebuilt in O(V) from the id attribute of the graph vertices. Vertices with an undefined or duplicated id are removed with their relations, with a warning. Test: two map entries of a saved file are swapped; the loaded relations used to be wrong.
  • fix(VertexIdentifier): repair unique vertices of loaded models: a mesh without (or with several) "unique vertices" attributes made the load throw. Unique vertices could also keep component vertices of missing components or with an out of range index. The missing attribute is now recreated and filled from the unique vertices. A new VertexIdentifierBuilder::repair_component_vertices, called after loading a BRep or a Section, removes component vertices of unregistered components, with an invalid vertex or linked to another unique vertex. It also restores missing links. Each repair logs a warning. Tests: the attribute is renamed in each mesh kind and checked to be restored; the vertices file of a model is used with the same model minus one corner, and that corner's entries are dropped.
  • fix(ComponentsStorage): repair loaded components: a missing components file was silently ignored even though save always writes it. It is now reported. Collections are the exception, since files written before they existed don't have them. After loading, null components are removed and components stored under another uuid than their own are moved back under their id, with a warning. Tests: a missing lines file is reported; a corner stored under a wrong key is found under its own id with its mesh.
  • fix(ModelIO): remove relations of components missing from loaded models: filter_unsupported_components now also removes the relations of supported-type components that don't exist in the model, with a warning. Test: the relationships file of a model is used with the same model minus one corner.
  • fix(ModelIO): load components with a missing mesh file or unknown mesh type: a missing mesh file made the load fail with a bare std::out_of_range (mapping.at). A mesh implementation not registered in the MeshFactory made MeshFactory::type throw. Now a missing file gives an empty mesh of the component's implementation, or of the default one if it is unknown; this also fixes mesh_type. An unknown implementation is replaced by the default implementation of the mesh type that matches the file extension. Both cases log a warning. Tests: a surface and a line mesh file are removed; the surface implementation name is replaced by an unknown one.
  • fix(ModelIO): save and load RegularGrid surface and block meshes: confirmed. Saving a Section with a RegularGrid surface, or a BRep with a RegularGrid block, threw "Cannot find the explicit SurfaceMesh type" because dispatch is done by dynamic_cast. Loading would have read the grid as a polygonal mesh. RegularGrid meshes now use the RegularGrid IO, for 2D surfaces and for blocks. Test: save and reload a Section with a grid surface and a BRep with a grid block.
  • fix(ModelIO): check output streams and use UTF-8 paths for model files: the components, relationships, unique vertices and physical properties files are now checked after opening and after flushing, like Identifier::Impl::save. They are opened with std::filesystem::u8path, and file_mapping uses u8string. Tests: saving each of these files into a missing directory now throws (it used to return silently); a save and reload round trip with a non-ASCII archive name. It already passed on Linux and failed on Windows until the UTF-8 commit below.
  • fix(Component): bound legacy component name size when loading: the Component::Impl v1 reader now uses MAX_SERIALIZED_NAME_SIZE instead of max_size(). Test: a component name longer than 1 MiB in an old file (layers.og_brep) is rejected; it used to load.
  • fix(ModelIO): restore the Logger level when a model load or save fails: the native BRep and Section input and output set the Logger level to warning and reset it by hand, so an exception left it stuck at warning. A new detail::ScopedLoggerLevel (in geode/basic/logger.hpp) restores the level on scope exit. Test: loading a model with a truncated relationships file throws and the level is restored. Known limitation, not fixed: the level is global, so concurrent loads or saves in different threads can still race on it.
  • fix(ModelIO): turn std exceptions into OpenGeodeException when loading: load_brep, load_section and load_brep_time_series now also catch std::exception and rethrow an OpenGeodeModelException with the usual "Cannot load ... from file" message, as fix(MeshIO): native IO and deserialization robustness #1361 did for meshes. Test: an input that throws std::runtime_error is registered for each of the three factories.
  • fix(ModelIO): check component existence without a qualified fold expression: follow-up to the relations commit. Clang and MSVC reject the fold expression ( model.Components::has_component( id ) || ... ), so the check now goes through a helper function template.
  • fix(IO): use UTF-8 paths in the generic input and output helpers: on Windows, the UTF-8 round trip test failed with "File ... does not exist". geode_object_input_impl checked the file with a narrow path, and geode_object_output_impl also created parent directories with a narrow path. Both now use std::filesystem::u8path, like ZipFile and Identifier. Test: the non-ASCII archive name round trip, which failed on Windows before.
  • fix(IO): only test non-ASCII archive names, not directories: CI showed that a non-ASCII directory still fails on Windows. The mesh files are written in a temporary directory next to the archive, and mesh outputs (BITSERY_WRITE...) still open narrow paths. Fixing that is mesh IO work outside this PR, so the test only covers archive names.

Notes

  • Known limitation, not fixed: native mesh IO (BITSERY_WRITE/BITSERY_READ, LightRegularGrid) still uses narrow paths, so models in non-ASCII directories cannot be saved or loaded on Windows.
  • UuidToIndex::update and erase(index_t) are left unchanged: they are on the removal paths that another PR works on, and loading no longer depends on them.
  • ComponentsStorage::load_components gets a defaulted required parameter, so downstream callers (OpenGeode-Geosciences horizons, faults...) build unchanged.
  • RelationshipsImpl::delete_isolated_vertices (protected, detail) is renamed repair_after_load. No downstream code uses it; StratigraphicRelationships gets the repair through its base class.

🤖 Generated with Claude Code

BotellaA and others added 10 commits October 20, 2026 10:05
The uuid -> graph vertex map was read from the file and trusted: an
inconsistent map silently returned the relations of another component,
and an out of range index was read out of bounds while removing isolated
vertices. The map is now rebuilt in O(V) from the component ids stored on
the graph vertices (still read for format compatibility). Vertices with an
undefined or duplicated id are removed with their relations, with a
warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Loading threw when a component mesh had no (or several) "unique vertices"
attribute, and unique vertices kept component vertices of components
missing from the model or with an out of range vertex index.
A missing attribute is now recreated and restored from the unique
vertices, with a warning. After loading a BRep or a Section, component
vertices of unknown components, with an invalid vertex or linked to
another unique vertex are removed, with a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A missing components file was silently ignored although it is always
saved: it is now reported, except for collections which may be missing
from files written before they existed. After deserialization, null
components are removed and components stored under another uuid than
their own are stored back under their id, with a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only relations of unsupported component types were removed after loading
a BRep or a Section. Relations of a component of a supported type that
does not exist in the model are now removed too, with a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h type

A missing mesh file made the load fail with a bare std::out_of_range, and
a mesh implementation not registered in the MeshFactory made it throw.
A component whose mesh file is missing now gets an empty mesh, and an
unknown implementation is replaced by the default implementation of the
mesh type matching the file extension, with a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Saving a model whose surface (2D) or block mesh is a RegularGrid threw
"Cannot find the explicit SurfaceMesh/SolidMesh type", and loading would
have read it as a polygonal surface or polyhedral solid. RegularGrid
meshes are now saved and loaded with the RegularGrid IO.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Saving the components, relationships, unique vertices and physical
properties files never checked the output stream: a file that could not
be written was silently skipped. The stream is now checked after opening
and after flushing, like Identifier::save. These files are also opened
with UTF-8 paths (std::filesystem::u8path), consistently with the
archive directory, and the mesh files mapping uses UTF-8 file names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first Component::Impl version read its name with no size limit
(max_size()). It now uses MAX_SERIALIZED_NAME_SIZE like other names, so a
corrupted size is rejected instead of allocating and reading gigabytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The native BRep and Section input and output lower the Logger level to
warning and restore it afterwards, but an exception skipped the restore
and left the level stuck. A new detail::ScopedLoggerLevel restores it on
scope exit. The level is still global: concurrent loads may race on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
load_brep, load_section and load_brep_time_series only caught
OpenGeodeException: std exceptions raised by an input escaped. They now
also catch std::exception and rethrow an OpenGeodeModelException with the
usual "Cannot load ... from file" message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@BotellaA
BotellaA requested a review from panquez October 20, 2026 08:25
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Cpp-Linter Report ⚠️

Some files did not pass the configured checks!

clang-tidy (v21.1.8) reports: 191 concern(s)
  • include/geode/basic/logger.hpp:42:31: warning: [cppcoreguidelines-special-member-functions]

    class 'Logger' defines a destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator

       42 |     class opengeode_basic_api Logger
          |                               ^
  • include/geode/model/mixin/builder/vertex_identifier_builder.hpp:79:49: warning: [readability-identifier-length]

    parameter name 'nb' is too short, expected at least 3 characters

       79 |         index_t create_unique_vertices( index_t nb );
          |                                                 ^
  • include/geode/model/mixin/core/detail/components_storage.hpp:214:39: warning: [cppcoreguidelines-avoid-magic-numbers]

    36 is a magic number; consider replacing it with a named constant

      214 |                 if( filename.size() > 36 )
          |                                       ^
  • include/geode/model/mixin/core/detail/components_storage.hpp:216:68: warning: [cppcoreguidelines-avoid-magic-numbers]

    36 is a magic number; consider replacing it with a named constant

      216 |                     auto uuid = filename.substr( filename.size() - 36 );
          |                                                                    ^
  • include/geode/model/mixin/core/detail/components_storage.hpp:244:14: warning: [readability-function-cognitive-complexity]

    function 'repair_loaded_components' has cognitive complexity of 12 (threshold 10)

      244 |         void repair_loaded_components( std::string_view filename )
          |              ^
    /__w/OpenGeode/OpenGeode/include/geode/model/mixin/core/detail/components_storage.hpp:247:13: note: +1, including nesting penalty of 0, nesting level increased to 1
      247 |             for( const auto& [component_id, component] : components_ )
          |             ^
    /__w/OpenGeode/OpenGeode/include/geode/model/mixin/core/detail/components_storage.hpp:249:17: note: +2, including nesting penalty of 1, nesting level increased to 2
      249 |                 if( !component || component->id() != component_id )
          |                 ^
    /__w/OpenGeode/OpenGeode/include/geode/model/mixin/core/detail/components_storage.hpp:249:32: note: +1
      249 |                 if( !component || component->id() != component_id )
          |                                ^
    /__w/OpenGeode/OpenGeode/include/geode/model/mixin/core/detail/components_storage.hpp:255:13: note: +1, including nesting penalty of 0, nesting level increased to 1
      255 |             if( !has_wrong_component )
          |             ^
    /__w/OpenGeode/OpenGeode/include/geode/model/mixin/core/detail/components_storage.hpp:261:13: note: +1, including nesting penalty of 0, nesting level increased to 1
      261 |             for( auto& [component_id, component] : components_ )
          |             ^
    /__w/OpenGeode/OpenGeode/include/geode/model/mixin/core/detail/components_storage.hpp:263:17: note: +2, including nesting penalty of 1, nesting level increased to 2
      263 |                 if( !component )
          |                 ^
    /__w/OpenGeode/OpenGeode/include/geode/model/mixin/core/detail/components_storage.hpp:271:17: note: +2, including nesting penalty of 1, nesting level increased to 2
      271 |                 if( id != component_id )
          |                 ^
    /__w/OpenGeode/OpenGeode/include/geode/model/mixin/core/detail/components_storage.hpp:280:17: note: +2, including nesting penalty of 1, nesting level increased to 2
      280 |                 if( !inserted )
          |                 ^
  • include/geode/model/mixin/core/detail/components_storage.hpp:270:22: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      270 |                 auto id = component->id();
          |                      ^
  • include/geode/model/mixin/core/detail/components_storage.hpp:293:24: warning: [readability-identifier-length]

    variable name 'it' is too short, expected at least 3 characters

      293 |             const auto it = components_.find( component_id );
          |                        ^
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:42:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       42 | namespace geode
          | ^~~~~~~~~~~~~~~
       43 | {
          | ~
       44 |     namespace detail
          |     ~~~~~~~~~~~~~~~~
          |     namespace geode::detail
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:53:13: warning: [google-explicit-constructor]

    single-argument constructors must be marked explicit to avoid unintentional implicit conversions

       53 |             RelationshipsImpl( BITSERY );
          |             ^
          |             explicit 
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:81:61: warning: [readability-identifier-length]

    parameter name 'to' is too short, expected at least 3 characters

       81 |                 const ComponentID& from, const ComponentID& to );
          |                                                             ^
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:94:57: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       94 |                 relation_components_from_index( index_t id ) const;
          |                                                         ^
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:144:60: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      144 |             index_t register_component( const ComponentID& id );
          |                                                            ^
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:147:25: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      147 |                 index_t id ) const;
          |                         ^
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:149:66: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      149 |             index_t find_or_create_vertex_id( const ComponentID& id );
          |                                                                  ^
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:156:61: warning: [readability-identifier-length]

    parameter name 'to' is too short, expected at least 3 characters

      156 |                 const ComponentID& from, const ComponentID& to );
          |                                                             ^
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:158:38: warning: [cppcoreguidelines-non-private-member-variables-in-classes]

    member variable 'graph_' has protected visibility

      158 |             std::unique_ptr< Graph > graph_;
          |                                      ^
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:159:33: warning: [cppcoreguidelines-non-private-member-variables-in-classes]

    member variable 'uuid2index_' has protected visibility

      159 |             detail::UuidToIndex uuid2index_;
          |                                 ^
  • include/geode/model/mixin/core/detail/relationships_impl.hpp:160:65: warning: [cppcoreguidelines-non-private-member-variables-in-classes]

    member variable 'ids_' has protected visibility

      160 |             std::shared_ptr< VariableAttribute< ComponentID > > ids_;
          |                                                                 ^
  • include/geode/model/mixin/core/detail/uuid_to_index.hpp:33:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       33 | namespace geode
          | ^~~~~~~~~~~~~~~
       34 | {
          | ~
       35 |     namespace detail
          |     ~~~~~~~~~~~~~~~~
          |     namespace geode::detail
  • include/geode/model/mixin/core/detail/uuid_to_index.hpp:45:71: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       45 |             [[nodiscard]] std::optional< index_t > index( const uuid& id ) const
          |                                                                       ^
  • include/geode/model/mixin/core/detail/uuid_to_index.hpp:55:37: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       55 |             void erase( const uuid& id )
          |                                     ^
  • include/geode/model/mixin/core/detail/uuid_to_index.hpp:62:28: warning: [readability-identifier-length]

    variable name 'it' is too short, expected at least 3 characters

       62 |                 for( auto& it : uuid2index_ )
          |                            ^
  • include/geode/model/mixin/core/detail/uuid_to_index.hpp:72:47: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       72 |             void set_new_mapping( const uuid& id, const index_t index )
          |                                               ^
  • include/geode/model/mixin/core/detail/uuid_to_index.hpp:89:28: warning: [readability-identifier-length]

    variable name 'it' is too short, expected at least 3 characters

       89 |                 for( auto& it : uuid2index_ )
          |                            ^
  • include/geode/model/mixin/core/detail/uuid_to_index.hpp:92:21: error: [clang-diagnostic-error]

    use of undeclared identifier 'OpenGeodeModelException'

       92 |                     OpenGeodeModelException::check_assertion(
          |                     ^
  • include/geode/model/mixin/core/detail/uuid_to_index.hpp:110:69: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      110 |                                        []( Archive& archive2, uuid& id,
          |                                                                     ^
  • include/geode/model/mixin/core/vertex_identifier.hpp:50:32: warning: [cppcoreguidelines-special-member-functions]

    class 'ComponentMeshVertex' defines a destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator

       50 |     struct opengeode_model_api ComponentMeshVertex
          |                                ^
  • include/geode/model/mixin/core/vertex_identifier.hpp:72:18: warning: [readability-identifier-naming]

    invalid case style for global function 'AbslHashValue'

       72 |         friend H AbslHashValue( H h, const ComponentMeshVertex& value )
          |                  ^~~~~~~~~~~~~
          |                  absl_hash_value
  • include/geode/model/mixin/core/vertex_identifier.hpp:72:35: warning: [readability-identifier-length]

    parameter name 'h' is too short, expected at least 3 characters

       72 |         friend H AbslHashValue( H h, const ComponentMeshVertex& value )
          |                                   ^
  • include/geode/model/mixin/core/vertex_identifier.hpp:91:31: warning: [cppcoreguidelines-special-member-functions]

    class 'VertexIdentifier' defines a destructor, a move constructor and a move assignment operator but does not define a copy constructor or a copy assignment operator

       91 |     class opengeode_model_api VertexIdentifier
          |                               ^
  • include/geode/model/mixin/core/vertex_identifier.hpp:96:9: warning: [google-explicit-constructor]

    single-argument constructors must be marked explicit to avoid unintentional implicit conversions

       96 |         VertexIdentifier( BITSERY );
          |         ^
          |         explicit 
  • include/geode/model/mixin/core/vertex_identifier.hpp:172:49: warning: [readability-identifier-length]

    parameter name 'nb' is too short, expected at least 3 characters

      172 |         index_t create_unique_vertices( index_t nb, BuilderKey /*key*/ );
          |                                                 ^
  • include/geode/model/representation/builder/detail/filter.hpp:62:13: warning: [modernize-use-nodiscard]

    function 'apply' should be marked [[nodiscard]]

       62 |             bool apply( const ComponentType& component_type ) const
          |             ^
          |             [[nodiscard]] 
  • src/geode/model/mixin/builder/vertex_identifier_builder.cpp:40:70: warning: [readability-identifier-length]

    parameter name 'nb' is too short, expected at least 3 characters

       40 |     index_t VertexIdentifierBuilder::create_unique_vertices( index_t nb )
          |                                                                      ^
  • src/geode/model/mixin/builder/vertex_identifier_builder.cpp:47:29: warning: [performance-unnecessary-value-param]

    the parameter 'component_vertex_id' is copied for each invocation but only used as a const reference; consider making it a const reference

       47 |         ComponentMeshVertex component_vertex_id, index_t unique_vertex_id )
          |                             ^
          |         const              &
  • src/geode/model/mixin/builder/vertex_identifier_builder.cpp:49:47: warning: [hicpp-move-const-arg]

    passing result of std::move() as a const reference argument; no move will actually happen

       49 |         vertex_identifier_.set_unique_vertex( std::move( component_vertex_id ),
          |                                               ^~~~~~~~~~                     ~
    /__w/OpenGeode/OpenGeode/include/geode/model/mixin/core/vertex_identifier.hpp:50:32: note: 'ComponentMeshVertex' is not move assignable/constructible
       50 |     struct opengeode_model_api ComponentMeshVertex
          |                                ^
  • src/geode/model/mixin/core/block_collections.cpp:58:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       58 |         const uuid& id ) const
          |                     ^
  • src/geode/model/mixin/core/block_collections.cpp:77:70: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       77 |         BlockCollections< dimension >::block_collection( const uuid& id ) const
          |                                                                      ^
  • src/geode/model/mixin/core/block_collections.cpp:85:25: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       85 |             const uuid& id, BlockCollectionsBuilderKey /*key*/ )
          |                         ^
  • src/geode/model/mixin/core/block_collections.cpp:137:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      137 |         const auto& id = collection->id();
          |                     ^
  • src/geode/model/mixin/core/block_collections.cpp:152:13: warning: [hicpp-move-const-arg]

    std::move of the variable 'block_collection_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      152 |             std::move( block_collection_id ) );
          |             ^~~~~~~~~~                     ~
  • src/geode/model/mixin/core/blocks.cpp:111:54: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      111 |     bool Blocks< dimension >::has_block( const uuid& id ) const
          |                                                      ^
  • src/geode/model/mixin/core/blocks.cpp:117:71: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      117 |     const Block< dimension >& Blocks< dimension >::block( const uuid& id ) const
          |                                                                       ^
  • src/geode/model/mixin/core/blocks.cpp:124:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      124 |         const uuid& id, BlocksBuilderKey /*unused*/ )
          |                     ^
  • src/geode/model/mixin/core/blocks.cpp:183:31: warning: [readability-function-cognitive-complexity]

    function 'load_blocks' has cognitive complexity of 11 (threshold 10)

      183 |     void Blocks< dimension >::load_blocks(
          |                               ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/blocks.cpp:190:9: note: +1, including nesting penalty of 0, nesting level increased to 1
      190 |         for( auto& block : modifiable_blocks( builder_key ) )
          |         ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/blocks.cpp:192:44: note: nesting level increased to 2
      192 |             tasks[count++] = async::spawn( [&block, &mapping] {
          |                                            ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/blocks.cpp:194:17: note: +3, including nesting penalty of 2, nesting level increased to 3
      194 |                 if( file == mapping.end() )
          |                 ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/blocks.cpp:206:17: note: +3, including nesting penalty of 2, nesting level increased to 3
      206 |                 if( type == TetrahedralSolid< dimension >::type_name_static() )
          |                 ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/blocks.cpp:212:22: note: +1, nesting level increased to 3
      212 |                 else if( type == HybridSolid< dimension >::type_name_static() )
          |                      ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/blocks.cpp:218:22: note: +1, nesting level increased to 3
      218 |                 else if( type == RegularGrid< dimension >::type_name_static() )
          |                      ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/blocks.cpp:224:17: note: +1, nesting level increased to 3
      224 |                 else
          |                 ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/blocks.cpp:232:9: note: +1, including nesting penalty of 0, nesting level increased to 1
      232 |         for( auto& task : async::when_all( tasks ).get() )
          |         ^
  • src/geode/model/mixin/core/blocks.cpp:244:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      244 |         const auto& id = block->id();
          |                     ^
  • src/geode/model/mixin/core/blocks.cpp:257:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      257 |         const auto& id = block->id();
          |                     ^
  • src/geode/model/mixin/core/blocks.cpp:269:45: warning: [hicpp-move-const-arg]

    std::move of the variable 'block_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      269 |         IdentifierBuilder{ *block }.set_id( std::move( block_id ) );
          |                                             ^~~~~~~~~~          ~
  • src/geode/model/mixin/core/blocks.cpp:281:45: warning: [hicpp-move-const-arg]

    std::move of the variable 'block_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      281 |         IdentifierBuilder{ *block }.set_id( std::move( block_id ) );
          |                                             ^~~~~~~~~~          ~
  • src/geode/model/mixin/core/component.cpp:41:9: warning: [modernize-use-nodiscard]

    function 'backward_compatible_name' should be marked [[nodiscard]]

       41 |         std::string_view backward_compatible_name() const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/component.cpp:46:9: warning: [modernize-use-nodiscard]

    function 'backward_compatible_id' should be marked [[nodiscard]]

       46 |         const uuid& backward_compatible_id() const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/component.cpp:51:9: warning: [modernize-use-nodiscard]

    function 'is_active' should be marked [[nodiscard]]

       51 |         bool is_active() const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/corner_collections.cpp:58:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       58 |         const uuid& id ) const
          |                     ^
  • src/geode/model/mixin/core/corner_collections.cpp:78:25: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       78 |             const uuid& id ) const
          |                         ^
  • src/geode/model/mixin/core/corner_collections.cpp:86:25: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       86 |             const uuid& id, CornerCollectionsBuilderKey /*key*/ )
          |                         ^
  • src/geode/model/mixin/core/corner_collections.cpp:138:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      138 |         const auto& id = collection->id();
          |                     ^
  • src/geode/model/mixin/core/corner_collections.cpp:153:13: warning: [hicpp-move-const-arg]

    std::move of the variable 'corner_collection_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      153 |             std::move( corner_collection_id ) );
          |             ^~~~~~~~~~                      ~
  • src/geode/model/mixin/core/corners.cpp:62:56: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       62 |     bool Corners< dimension >::has_corner( const uuid& id ) const
          |                                                        ^
  • src/geode/model/mixin/core/corners.cpp:81:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       81 |         const uuid& id ) const
          |                     ^
  • src/geode/model/mixin/core/corners.cpp:88:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       88 |         const uuid& id, CornersBuilderKey /*key*/ )
          |                     ^
  • src/geode/model/mixin/core/corners.cpp:178:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      178 |         const auto& id = corner->id();
          |                     ^
  • src/geode/model/mixin/core/corners.cpp:191:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      191 |         const auto& id = corner->id();
          |                     ^
  • src/geode/model/mixin/core/corners.cpp:204:46: warning: [hicpp-move-const-arg]

    std::move of the variable 'corner_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      204 |         IdentifierBuilder{ *corner }.set_id( std::move( corner_id ) );
          |                                              ^~~~~~~~~~           ~
  • src/geode/model/mixin/core/corners.cpp:216:46: warning: [hicpp-move-const-arg]

    std::move of the variable 'corner_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      216 |         IdentifierBuilder{ *corner }.set_id( std::move( corner_id ) );
          |                                              ^~~~~~~~~~           ~
  • src/geode/model/mixin/core/detail/relationships_impl.cpp:43:1: warning: [modernize-concat-nested-namespaces]

    nested namespaces can be concatenated

       43 | namespace geode
          | ^~~~~~~~~~~~~~~
       44 | {
          | ~
       45 |     namespace detail
          |     ~~~~~~~~~~~~~~~~
          |     namespace geode::detail
  • src/geode/model/mixin/core/detail/relationships_impl.cpp:52:55: warning: [hicpp-named-parameter]

    all parameters should be named in a function

       52 |         RelationshipsImpl::RelationshipsImpl( BITSERY ) {}
          |                                                       ^
          |                                                        /*unused*/
  • src/geode/model/mixin/core/detail/relationships_impl.cpp:131:57: warning: [readability-identifier-length]

    parameter name 'to' is too short, expected at least 3 characters

      131 |             const ComponentID& from, const ComponentID& to )
          |                                                         ^
  • src/geode/model/mixin/core/detail/relationships_impl.cpp:144:57: warning: [readability-identifier-length]

    parameter name 'to' is too short, expected at least 3 characters

      144 |             const ComponentID& from, const ComponentID& to )
          |                                                         ^
  • src/geode/model/mixin/core/detail/relationships_impl.cpp:274:24: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      274 |             const auto id =
          |                        ^
  • src/geode/model/mixin/core/detail/relationships_impl.cpp:312:33: warning: [readability-function-cognitive-complexity]

    function 'repair_after_load' has cognitive complexity of 21 (threshold 10)

      312 |         void RelationshipsImpl::repair_after_load()
          |                                 ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:317:13: note: +1, including nesting penalty of 0, nesting level increased to 1
      317 |             if( !ids_ )
          |             ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:329:13: note: +1, including nesting penalty of 0, nesting level increased to 1
      329 |             for( const auto vertex : Range{ nb_vertices } )
          |             ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:332:17: note: +2, including nesting penalty of 1, nesting level increased to 2
      332 |                 if( component_id.type == undefined_type )
          |                 ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:338:17: note: +2, including nesting penalty of 1, nesting level increased to 2
      338 |                 if( !valid_ids.emplace( component_id.id ).second )
          |                 ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:344:17: note: +2, including nesting penalty of 1, nesting level increased to 2
      344 |                 if( uuid2index_.index( component_id.id ) != vertex )
          |                 ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:349:13: note: +1, including nesting penalty of 0, nesting level increased to 1
      349 |             if( nb_wrong_mappings > 0
          |             ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:350:17: note: +1
      350 |                 || uuid2index_.nb_mappings() != valid_ids.size() )
          |                 ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:355:13: note: +1, including nesting penalty of 0, nesting level increased to 1
      355 |             if( nb_undefined > 0 || nb_duplicated > 0 )
          |             ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:355:34: note: +1
      355 |             if( nb_undefined > 0 || nb_duplicated > 0 )
          |                                  ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:363:17: note: +2, including nesting penalty of 1, nesting level increased to 2
      363 |                 for( const auto vertex : Range{ nb_vertices } )
          |                 ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:365:21: note: +3, including nesting penalty of 2, nesting level increased to 3
      365 |                     if( !vertices_to_delete[vertex] )
          |                     ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:369:21: note: +3, including nesting penalty of 2, nesting level increased to 3
      369 |                     for( const auto& edge :
          |                     ^
    /__w/OpenGeode/OpenGeode/src/geode/model/mixin/core/detail/relationships_impl.cpp:381:13: note: +1, including nesting penalty of 0, nesting level increased to 1
      381 |             for( const auto vertex : Range{ graph_->nb_vertices() } )
          |             ^
  • src/geode/model/mixin/core/line_collections.cpp:58:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       58 |         const uuid& id ) const
          |                     ^
  • src/geode/model/mixin/core/line_collections.cpp:77:68: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       77 |         LineCollections< dimension >::line_collection( const uuid& id ) const
          |                                                                    ^
  • src/geode/model/mixin/core/line_collections.cpp:85:25: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       85 |             const uuid& id, LineCollectionsBuilderKey /*key*/ )
          |                         ^
  • src/geode/model/mixin/core/line_collections.cpp:137:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      137 |         const auto& id = collection->id();
          |                     ^
  • src/geode/model/mixin/core/line_collections.cpp:150:13: warning: [hicpp-move-const-arg]

    std::move of the variable 'line_collection_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      150 |             std::move( line_collection_id ) );
          |             ^~~~~~~~~~                    ~
  • src/geode/model/mixin/core/lines.cpp:73:52: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       73 |     bool Lines< dimension >::has_line( const uuid& id ) const
          |                                                    ^
  • src/geode/model/mixin/core/lines.cpp:79:68: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       79 |     const Line< dimension >& Lines< dimension >::line( const uuid& id ) const
          |                                                                    ^
  • src/geode/model/mixin/core/lines.cpp:86:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       86 |         const uuid& id, LinesBuilderKey /*key*/ )
          |                     ^
  • src/geode/model/mixin/core/lines.cpp:174:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      174 |         const auto& id = line->id();
          |                     ^
  • src/geode/model/mixin/core/lines.cpp:187:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      187 |         const auto& id = line->id();
          |                     ^
  • src/geode/model/mixin/core/lines.cpp:199:44: warning: [hicpp-move-const-arg]

    std::move of the variable 'line_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      199 |         IdentifierBuilder{ *line }.set_id( std::move( line_id ) );
          |                                            ^~~~~~~~~~         ~
  • src/geode/model/mixin/core/lines.cpp:211:44: warning: [hicpp-move-const-arg]

    std::move of the variable 'line_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      211 |         IdentifierBuilder{ *line }.set_id( std::move( line_id ) );
          |                                            ^~~~~~~~~~         ~
  • src/geode/model/mixin/core/physical_properties.cpp:52:62: warning: [readability-identifier-length]

    parameter name 'a' is too short, expected at least 3 characters

       52 |             *this, Growable< Archive, Info >{ { []( Archive& a, Info& info ) {
          |                                                              ^
  • src/geode/model/mixin/core/physical_properties.cpp:63:9: warning: [google-explicit-constructor]

    single-argument constructors must be marked explicit to avoid unintentional implicit conversions

       63 |         Impl( BITSERY ) {}
          |         ^
          |         explicit 
  • src/geode/model/mixin/core/physical_properties.cpp:63:23: warning: [hicpp-named-parameter]

    all parameters should be named in a function

       63 |         Impl( BITSERY ) {}
          |                       ^
          |                        /*unused*/
  • src/geode/model/mixin/core/physical_properties.cpp:89:27: warning: [hicpp-move-const-arg]

    std::move of the variable 'attribute_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

       89 |                           std::move( attribute_id ) } );
          |                           ^~~~~~~~~~              ~
  • src/geode/model/mixin/core/physical_properties.cpp:151:59: warning: [readability-identifier-length]

    parameter name 'a' is too short, expected at least 3 characters

      151 |                 Growable< Archive, Impl >{ { []( Archive& a, Impl& impl ) {
          |                                                           ^
  • src/geode/model/mixin/core/physical_properties.cpp:154:38: warning: [readability-identifier-length]

    parameter name 'a2' is too short, expected at least 3 characters

      154 |                         []( Archive& a2, PHYSICAL_PROPERTY_NAME& name,
          |                                      ^
  • src/geode/model/mixin/core/physical_properties.cpp:213:48: warning: [hicpp-move-const-arg]

    std::move of the variable 'attribute_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      213 |             name, std::move( component_type ), std::move( attribute_id ) );
          |                                                ^~~~~~~~~~              ~
  • src/geode/model/mixin/core/relationships.cpp:62:18: warning: [readability-redundant-member-init]

    initializer for base class 'detail::RelationshipsImpl' is redundant

       62 |         Impl() : RelationshipsImpl()
          |                  ^~~~~~~~~~~~~~~~~~~
  • src/geode/model/mixin/core/relationships.cpp:67:9: warning: [google-explicit-constructor]

    single-argument constructors must be marked explicit to avoid unintentional implicit conversions

       67 |         Impl( BITSERY bitsery ) : RelationshipsImpl( bitsery ) {}
          |         ^
          |         explicit 
  • src/geode/model/mixin/core/relationships.cpp:69:9: warning: [modernize-use-nodiscard]

    function 'relation_type' should be marked [[nodiscard]]

       69 |         RelationType relation_type( const index_t edge_id ) const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/relationships.cpp:83:17: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

       83 |                 ++current;
          |                 ^
  • src/geode/model/mixin/core/relationships.cpp:87:9: warning: [modernize-use-nodiscard]

    function 'is_boundary_relation' should be marked [[nodiscard]]

       87 |         bool is_boundary_relation( const index_t edge_id ) const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/relationships.cpp:92:9: warning: [modernize-use-nodiscard]

    function 'is_internal_relation' should be marked [[nodiscard]]

       92 |         bool is_internal_relation( const index_t edge_id ) const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/relationships.cpp:97:9: warning: [modernize-use-nodiscard]

    function 'is_item_relation' should be marked [[nodiscard]]

       97 |         bool is_item_relation( const index_t edge_id ) const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/relationships.cpp:102:9: warning: [modernize-use-nodiscard]

    function 'check_relation_exists' should be marked [[nodiscard]]

      102 |         std::optional< index_t > check_relation_exists(
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/relationships.cpp:103:43: warning: [readability-identifier-length]

    parameter name 'to' is too short, expected at least 3 characters

      103 |             const uuid& from, const uuid& to, const RelationType type ) const
          |                                           ^
  • src/geode/model/mixin/core/relationships.cpp:115:9: warning: [modernize-use-nodiscard]

    function 'is_boundary' should be marked [[nodiscard]]

      115 |         bool is_boundary( const uuid& from, const uuid& to ) const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/relationships.cpp:115:57: warning: [readability-identifier-length]

    parameter name 'to' is too short, expected at least 3 characters

      115 |         bool is_boundary( const uuid& from, const uuid& to ) const
          |                                                         ^
  • src/geode/model/mixin/core/relationships.cpp:129:9: warning: [modernize-use-nodiscard]

    function 'is_internal' should be marked [[nodiscard]]

      129 |         bool is_internal( const uuid& from, const uuid& to ) const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/relationships.cpp:129:57: warning: [readability-identifier-length]

    parameter name 'to' is too short, expected at least 3 characters

      129 |         bool is_internal( const uuid& from, const uuid& to ) const
          |                                                         ^
  • src/geode/model/mixin/core/relationships.cpp:143:9: warning: [modernize-use-nodiscard]

    function 'is_item' should be marked [[nodiscard]]

      143 |         bool is_item( const uuid& from, const uuid& to ) const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/relationships.cpp:143:53: warning: [readability-identifier-length]

    parameter name 'to' is too short, expected at least 3 characters

      143 |         bool is_item( const uuid& from, const uuid& to ) const
          |                                                     ^
  • src/geode/model/mixin/core/relationships.cpp:157:32: warning: [readability-identifier-length]

    parameter name 'to' is too short, expected at least 3 characters

      157 |             const ComponentID& to,
          |                                ^
  • src/geode/model/mixin/core/relationships.cpp:281:24: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      281 |             const auto id =
          |                        ^
  • src/geode/model/mixin/core/relationships.cpp:291:9: warning: [modernize-use-nodiscard]

    function 'relation_to_string' should be marked [[nodiscard]]

      291 |         std::string relation_to_string( index_t relation_type ) const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/relationships.cpp:488:9: warning: [readability-avoid-return-with-void-value]

    return statement within a void function should not have a specified return value

      488 |         return impl_->load( directory );
          |         ^~~~~~
  • src/geode/model/mixin/core/relationships.cpp:515:29: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      515 |         end_ = edges.data() + edges.size();
          |                             ^
  • src/geode/model/mixin/core/relationships.cpp:534:9: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      534 |         ++current_;
          |         ^
  • src/geode/model/mixin/core/relationships.cpp:549:29: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      549 |         end_ = edges.data() + edges.size();
          |                             ^
  • src/geode/model/mixin/core/relationships.cpp:571:9: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      571 |         ++current_;
          |         ^
  • src/geode/model/mixin/core/relationships.cpp:589:29: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      589 |         end_ = edges.data() + edges.size();
          |                             ^
  • src/geode/model/mixin/core/relationships.cpp:611:9: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      611 |         ++current_;
          |         ^
  • src/geode/model/mixin/core/relationships.cpp:629:29: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      629 |         end_ = edges.data() + edges.size();
          |                             ^
  • src/geode/model/mixin/core/relationships.cpp:651:9: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      651 |         ++current_;
          |         ^
  • src/geode/model/mixin/core/relationships.cpp:669:29: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      669 |         end_ = edges.data() + edges.size();
          |                             ^
  • src/geode/model/mixin/core/relationships.cpp:691:9: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      691 |         ++current_;
          |         ^
  • src/geode/model/mixin/core/relationships.cpp:709:29: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      709 |         end_ = edges.data() + edges.size();
          |                             ^
  • src/geode/model/mixin/core/relationships.cpp:730:9: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      730 |         ++current_;
          |         ^
  • src/geode/model/mixin/core/relationships.cpp:747:29: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      747 |         end_ = edges.data() + edges.size();
          |                             ^
  • src/geode/model/mixin/core/relationships.cpp:769:9: warning: [cppcoreguidelines-pro-bounds-pointer-arithmetic]

    do not use pointer arithmetic

      769 |         ++current_;
          |         ^
  • src/geode/model/mixin/core/surface_collections.cpp:58:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       58 |         const uuid& id ) const
          |                     ^
  • src/geode/model/mixin/core/surface_collections.cpp:79:25: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       79 |             const uuid& id ) const
          |                         ^
  • src/geode/model/mixin/core/surface_collections.cpp:87:25: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

       87 |             const uuid& id, SurfaceCollectionsBuilderKey /*key*/ )
          |                         ^
  • src/geode/model/mixin/core/surface_collections.cpp:140:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      140 |         const auto& id = collection->id();
          |                     ^
  • src/geode/model/mixin/core/surface_collections.cpp:154:13: warning: [hicpp-move-const-arg]

    std::move of the variable 'surface_collection_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      154 |             std::move( surface_collection_id ) );
          |             ^~~~~~~~~~                       ~
  • src/geode/model/mixin/core/surfaces.cpp:137:58: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      137 |     bool Surfaces< dimension >::has_surface( const uuid& id ) const
          |                                                          ^
  • src/geode/model/mixin/core/surfaces.cpp:144:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      144 |         const uuid& id ) const
          |                     ^
  • src/geode/model/mixin/core/surfaces.cpp:151:21: warning: [readability-identifier-length]

    parameter name 'id' is too short, expected at least 3 characters

      151 |         const uuid& id, SurfacesBuilderKey /*key*/ )
          |                     ^
  • src/geode/model/mixin/core/surfaces.cpp:278:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      278 |         const auto& id = surface->id();
          |                     ^
  • src/geode/model/mixin/core/surfaces.cpp:291:21: warning: [readability-identifier-length]

    variable name 'id' is too short, expected at least 3 characters

      291 |         const auto& id = surface->id();
          |                     ^
  • src/geode/model/mixin/core/surfaces.cpp:304:47: warning: [hicpp-move-const-arg]

    std::move of the variable 'surface_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      304 |         IdentifierBuilder{ *surface }.set_id( std::move( surface_id ) );
          |                                               ^~~~~~~~~~            ~
  • src/geode/model/mixin/core/surfaces.cpp:316:47: warning: [hicpp-move-const-arg]

    std::move of the variable 'surface_id' of the trivially-copyable type 'uuid' has no effect; remove std::move()

      316 |         IdentifierBuilder{ *surface }.set_id( std::move( surface_id ) );
          |                                               ^~~~~~~~~~            ~
  • src/geode/model/mixin/core/vertex_identifier.cpp:152:9: warning: [google-explicit-constructor]

    single-argument constructors must be marked explicit to avoid unintentional implicit conversions

      152 |         Impl( BITSERY ) {}
          |         ^
          |         explicit 
  • src/geode/model/mixin/core/vertex_identifier.cpp:152:23: warning: [hicpp-named-parameter]

    all parameters should be named in a function

      152 |         Impl( BITSERY ) {}
          |                       ^
          |                        /*unused*/
  • src/geode/model/mixin/core/vertex_identifier.cpp:154:9: warning: [modernize-use-nodiscard]

    function 'nb_unique_vertices' should be marked [[nodiscard]]

      154 |         index_t nb_unique_vertices() const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/vertex_identifier.cpp:159:9: warning: [modernize-use-nodiscard]

    function 'is_unique_vertex_isolated' should be marked [[nodiscard]]

      159 |         bool is_unique_vertex_isolated( index_t unique_vertex_id ) const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/vertex_identifier.cpp:164:9: warning: [modernize-use-nodiscard]

    function 'component_mesh_vertices' should be marked [[nodiscard]]

      164 |         const std::vector< ComponentMeshVertex >& component_mesh_vertices(
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/vertex_identifier.cpp:175:9: warning: [modernize-use-nodiscard]

    function 'unique_vertex' should be marked [[nodiscard]]

      175 |         index_t unique_vertex(
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/vertex_identifier.cpp:182:9: warning: [modernize-use-nodiscard]

    function 'has_component_mesh_vertices' should be marked [[nodiscard]]

      182 |         bool has_component_mesh_vertices(
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/vertex_identifier.cpp:197:9: warning: [modernize-use-nodiscard]

    function 'has_component_mesh_vertices' should be marked [[nodiscard]]

      197 |         bool has_component_mesh_vertices(
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/vertex_identifier.cpp:211:9: warning: [modernize-use-nodiscard]

    function 'unique_vertex_attribute_id' should be marked [[nodiscard]]

      211 |         const uuid& unique_vertex_attribute_id() const
          |         ^
          |         [[nodiscard]] 
  • src/geode/model/mixin/core/vertex_identifier.cpp:267:24: warning: [readability-identifier-length]

    variable name 'it' is too short, expected at least 3 characters

      267 |             const auto it = vertex2unique_vertex_.find( component.id() );
          |                        ^
  • src/geode/model/mixin/core/vertex_identifier.cpp:286:55: warning: [readability-identifier-length]

    parameter name 'nb' is too short, expected at least 3 characters

      286 |         index_t create_unique_vertices( const index_t nb )
          |                                                       ^

Have any feedback or feature suggestions? Share it here.

BotellaA and others added 4 commits October 20, 2026 10:39
…ession

Clang and MSVC reject a fold expression over qualified member calls
(model.Components::has_component). The check now goes through a helper
function template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
geode_object_input_impl checked the file existence and
geode_object_output_impl created the parent directories with narrow
paths. On Windows, a non-ASCII (UTF-8) file name was reported as missing
when loading and directories got a wrong name when saving. Both now use
std::filesystem::u8path, consistently with ZipFile and Identifier.
Test: saving and loading a BRep with a non-ASCII archive name and
directory, which failed on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model mesh files are written in a temporary directory next to the
archive by mesh outputs that still open narrow paths, so a non-ASCII
directory fails on Windows. That is a mesh IO limitation outside this
change; the UTF-8 test now only covers non-ASCII archive names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pairing them

repair_component_vertices ran on every model load, serially over all unique
vertices, with one allocation per unique vertex. A read-only parallel scan now
flags the unique vertices to repair, and only those are repaired sequentially,
so valid models skip all allocations and writes. The repair result is
unchanged: it only fills NO_ID links, which never belong to unflagged unique
vertices.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment on lines +448 to +471
if( nb_unknown_components > 0 )
{
Logger::warning( "[VertexIdentifier::load] Removed ",
nb_unknown_components,
" component vertices of unknown components" );
}
if( nb_invalid_vertices > 0 )
{
Logger::warning( "[VertexIdentifier::load] Removed ",
nb_invalid_vertices,
" component vertices with invalid vertex index" );
}
if( nb_inconsistent_vertices > 0 )
{
Logger::warning( "[VertexIdentifier::load] Removed ",
nb_inconsistent_vertices,
" component vertices linked to another unique vertex" );
}
if( nb_restored_vertices > 0 )
{
Logger::warning( "[VertexIdentifier::load] Restored ",
nb_restored_vertices,
" component vertex links to their unique vertex" );
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

put this warning raisors if-branches into a dedicated function

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done: the warnings are now grouped in ComponentVerticesRepair::log().

{
if( !component )
{
Logger::warning( "[ComponentsStorage::load_components] "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if it should be a simple warning, or an exception raising?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Kept as a warning on purpose: this runs on load, and our policy for corrupted files is to repair what can be repaired and warn, rather than reject the whole model. Here the invalid component is dropped, so the loaded model stays consistent.

else if( !existence_checker.apply( model, component_id.id ) )
{
Logger::warning( "[filter_unsupported_components] "
"Removing relations of missing ",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same comment.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Same rationale: the relations pointing to missing components are removed during load, so the model is repaired and usable; a warning reports it instead of failing the whole load.

# Conflicts:
#	src/geode/model/mixin/core/vertex_identifier.cpp
# Conflicts:
#	include/geode/model/mixin/core/detail/components_storage.hpp
@BenPinet
BenPinet merged commit 6d1a7a6 into next Oct 21, 2026
23 checks passed
@BenPinet
BenPinet deleted the fix/model-io-robustness branch October 21, 2026 13:26
@BotellaA

Copy link
Copy Markdown
Member Author

🎉 This PR is included in version 19.1.0-rc.7 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@geode-ci

geode-ci Bot commented Oct 22, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 19.1.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@geode-ci geode-ci Bot added the released label Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants