LCOV - code coverage report
Current view: top level - spb-proto-compiler/ast - ast-types.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 90.9 % 143 130
Test Date: 2025-05-23 14:18:14 Functions: 100.0 % 25 25

            Line data    Source code
       1              : /***************************************************************************\
       2              : * Name        : ast render                                                  *
       3              : * Description : resolve types in ast tree                                   *
       4              : * Author      : antonin.kriz@gmail.com                                      *
       5              : * ------------------------------------------------------------------------- *
       6              : * This is free software; you can redistribute it and/or modify it under the *
       7              : * terms of the MIT license. A copy of the license can be found in the file  *
       8              : * "LICENSE" at the root of this distribution.                               *
       9              : \***************************************************************************/
      10              : 
      11              : #include "ast/proto-oneof.h"
      12              : 
      13              : #include "../dumper/header.h"
      14              : #include "../parser/options.h"
      15              : #include "proto-field.h"
      16              : #include <algorithm>
      17              : #include "proto-file.h"
      18              : #include "proto-message.h"
      19              : #include "spb/pb/wire-types.h"
      20              : #include <array>
      21              : #include <string_view>
      22              : #include <utility>
      23              : 
      24              : namespace
      25              : {
      26              : using namespace std::literals;
      27              : 
      28              : struct search_ctx
      29              : {
      30              :     //- `file` containing `message`
      31              :     const proto_file & file;
      32              :     //- `message` containing `field`
      33              :     const proto_message & message;
      34              :     //- parent message (null for top level)
      35              :     const search_ctx * p_parent = nullptr;
      36              : };
      37              : 
      38              : using scalar_encoder = spb::pb::detail::scalar_encoder;
      39              : 
      40         1391 : [[nodiscard]] auto type_parts( std::string_view type_name ) -> size_t
      41              : {
      42         1391 :     return size_t( std::count( type_name.cbegin( ), type_name.cend( ), '.' ) );
      43              : }
      44              : 
      45         1389 : [[nodiscard]] auto is_last_part( const proto_field & field, size_t type_part ) -> bool
      46              : {
      47         1389 :     return type_part == type_parts( field.type_name );
      48              : }
      49              : 
      50         1744 : [[nodiscard]] auto get_type_part( const proto_field & field, size_t type_part ) -> std::string_view
      51              : {
      52         1744 :     auto type_name = field.type_name;
      53         1821 :     for( ; type_part > 0; type_part-- )
      54              :     {
      55           77 :         const auto dot_index = type_name.find( '.' );
      56           77 :         if( dot_index == type_name.npos )
      57            0 :             return { };
      58              : 
      59           77 :         type_name.remove_prefix( dot_index + 1 );
      60              :     }
      61              : 
      62         1744 :     return type_name.substr( 0, type_name.find( '.' ) );
      63              : }
      64              : 
      65              : [[nodiscard]] auto resolve_type( const search_ctx & self, const proto_field & field,
      66              :                                  size_t type_part )
      67              :     -> std::pair< proto_field::Type, proto_field::BitType >;
      68              : 
      69          114 : [[nodiscard]] auto remove_bitfield( std::string_view type ) -> std::string_view
      70              : {
      71          114 :     return type.substr( 0, type.find( ':' ) );
      72              : }
      73              : 
      74          114 : [[nodiscard]] auto convertible_types( proto_field::Type from, proto_field::BitType to ) -> bool
      75              : {
      76              :     static constexpr auto type_map =
      77              :         std::array< std::pair< proto_field::Type, std::array< proto_field::BitType, 4 > >, 15 >{ {
      78              :             { proto_field::Type::INT32,
      79              :               { { proto_field::BitType::INT8, proto_field::BitType::INT16,
      80              :                   proto_field::BitType::INT32 } } },
      81              :             { proto_field::Type::INT64,
      82              :               { { proto_field::BitType::INT8, proto_field::BitType::INT16,
      83              :                   proto_field::BitType::INT32, proto_field::BitType::INT64 } } },
      84              :             { proto_field::Type::SINT32,
      85              :               { { proto_field::BitType::INT8, proto_field::BitType::INT16,
      86              :                   proto_field::BitType::INT32 } } },
      87              :             { proto_field::Type::SINT64,
      88              :               { { proto_field::BitType::INT8, proto_field::BitType::INT16,
      89              :                   proto_field::BitType::INT32, proto_field::BitType::INT64 } } },
      90              :             { proto_field::Type::UINT32,
      91              :               { { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
      92              :                   proto_field::BitType::UINT32 } } },
      93              :             { proto_field::Type::UINT64,
      94              :               { { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
      95              :                   proto_field::BitType::UINT32, proto_field::BitType::UINT64 } } },
      96              :             { proto_field::Type::FIXED32,
      97              :               { { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
      98              :                   proto_field::BitType::UINT32 } } },
      99              :             { proto_field::Type::FIXED64,
     100              :               { { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
     101              :                   proto_field::BitType::UINT32, proto_field::BitType::UINT64 } } },
     102              :             { proto_field::Type::SFIXED32,
     103              :               { { proto_field::BitType::INT8, proto_field::BitType::INT16,
     104              :                   proto_field::BitType::INT32 } } },
     105              :             { proto_field::Type::SFIXED64,
     106              :               { { proto_field::BitType::INT8, proto_field::BitType::INT16,
     107              :                   proto_field::BitType::INT32, proto_field::BitType::INT64 } } },
     108              :         } };
     109              : 
     110          722 :     for( auto [ proto_type, types ] : type_map )
     111              :     {
     112          722 :         if( from == proto_type )
     113          114 :             return std::find( types.begin( ), types.end( ), to ) != types.end( );
     114              :     }
     115            0 :     return false;
     116              : }
     117              : 
     118          114 : [[nodiscard]] auto get_scalar_bit_type( std::string_view type_name ) -> proto_field::BitType
     119              : {
     120              :     struct type_table
     121              :     {
     122              :         std::string_view name;
     123              :         proto_field::BitType type;
     124              :     };
     125              : 
     126              :     static constexpr auto scalar_types = std::array< type_table, 8 >{ {
     127              :         { "int8"sv, proto_field::BitType::INT8 },
     128              :         { "int16"sv, proto_field::BitType::INT16 },
     129              :         { "int32"sv, proto_field::BitType::INT32 },
     130              :         { "int64"sv, proto_field::BitType::INT64 },
     131              :         { "uint8"sv, proto_field::BitType::UINT8 },
     132              :         { "uint16"sv, proto_field::BitType::UINT16 },
     133              :         { "uint32"sv, proto_field::BitType::UINT32 },
     134              :         { "uint64"sv, proto_field::BitType::UINT64 },
     135              :     } };
     136              : 
     137          416 :     for( const auto item : scalar_types )
     138              :     {
     139          416 :         if( item.name == type_name )
     140          114 :             return item.type;
     141              :     }
     142              : 
     143            0 :     return proto_field::BitType::NONE;
     144              : }
     145              : 
     146         1769 : [[nodiscard]] auto get_scalar_proto_type( std::string_view type_name ) -> proto_field::Type
     147              : {
     148              :     struct type_table
     149              :     {
     150              :         std::string_view name;
     151              :         proto_field::Type type;
     152              :     };
     153              : 
     154              :     static constexpr auto scalar_types = std::array< type_table, 15 >{ {
     155              :         { "bool"sv, proto_field::Type::BOOL },
     156              :         { "bytes"sv, proto_field::Type::BYTES },
     157              :         { "double"sv, proto_field::Type::DOUBLE },
     158              :         { "float"sv, proto_field::Type::FLOAT },
     159              :         { "int32"sv, proto_field::Type::INT32 },
     160              :         { "int64"sv, proto_field::Type::INT64 },
     161              :         { "uint32"sv, proto_field::Type::UINT32 },
     162              :         { "uint64"sv, proto_field::Type::UINT64 },
     163              :         { "sint32"sv, proto_field::Type::SINT32 },
     164              :         { "sint64"sv, proto_field::Type::SINT64 },
     165              :         { "fixed32"sv, proto_field::Type::FIXED32 },
     166              :         { "fixed64"sv, proto_field::Type::FIXED64 },
     167              :         { "sfixed32"sv, proto_field::Type::SFIXED32 },
     168              :         { "sfixed64"sv, proto_field::Type::SFIXED64 },
     169              :         { "string"sv, proto_field::Type::STRING },
     170              :     } };
     171              : 
     172        22466 :     for( const auto item : scalar_types )
     173              :     {
     174        21522 :         if( item.name == type_name )
     175          825 :             return item.type;
     176              :     }
     177              : 
     178          944 :     return proto_field::Type::NONE;
     179              : }
     180              : 
     181         1769 : [[nodiscard]] auto get_field_type( const proto_file & file, const proto_field & field )
     182              :     -> std::pair< proto_field::Type, proto_field::BitType >
     183              : {
     184         1769 :     const auto type = get_scalar_proto_type( field.type_name );
     185         1769 :     if( type == proto_field::Type::NONE )
     186          944 :         return { };
     187              : 
     188          825 :     if( const auto p_name = field.options.find( option_field_type );
     189          825 :         p_name != field.options.end( ) )
     190              :     {
     191          114 :         const auto field_type = get_scalar_bit_type( remove_bitfield( p_name->second ) );
     192          114 :         if( !convertible_types( type, field_type ) )
     193              :         {
     194            0 :             throw_parse_error( file, p_name->second,
     195            0 :                                std::string( "incompatible int type: " ) +
     196            0 :                                    std::string( field.type_name ) + " and " +
     197            0 :                                    std::string( p_name->second ) );
     198              :         }
     199          114 :         return { type, field_type };
     200              :     }
     201              : 
     202          711 :     return { type, proto_field::BitType::NONE };
     203              : }
     204              : 
     205         1769 : [[nodiscard]] auto get_scalar_type( const proto_file & file, const proto_field & field,
     206              :                                     size_t type_part )
     207              :     -> std::pair< proto_field::Type, proto_field::BitType >
     208              : {
     209         1769 :     if( type_part != 0 )
     210            0 :         return { };
     211              : 
     212         1769 :     return get_field_type( file, field );
     213              : }
     214              : 
     215          850 : [[nodiscard]] auto get_sub_message( const proto_message & message, const proto_field & field,
     216              :                                     size_t type_part ) -> const proto_message *
     217              : {
     218          850 :     const auto type_name = get_type_part( field, type_part );
     219          850 :     const auto index     = std::find_if( message.messages.begin( ), message.messages.end( ),
     220         8727 :                                          [ type_name ]( const auto & sub_message ) -> bool
     221         8727 :                                          { return type_name == sub_message.name; } );
     222          850 :     return ( index != message.messages.end( ) ) ? &*index : nullptr;
     223              : }
     224              : 
     225          983 : [[nodiscard]] auto resolve_enum( const proto_message & message, const proto_field & field,
     226              :                                  size_t type_part ) -> proto_field::Type
     227              : {
     228          983 :     if( !is_last_part( field, type_part ) )
     229           89 :         return proto_field::Type::NONE;
     230              : 
     231          894 :     const auto type_name = get_type_part( field, type_part );
     232              : 
     233          894 :     return std::any_of( message.enums.begin( ), message.enums.end( ),
     234         1122 :                         [ type_name ]( const auto & enum_ ) -> bool
     235         1122 :                         { return type_name == enum_.name; } )
     236          894 :         ? proto_field::Type::ENUM
     237          894 :         : proto_field::Type::NONE;
     238              : }
     239              : 
     240          983 : [[nodiscard]] auto resolve_from_message( const proto_message & message, const proto_field & field,
     241              :                                          size_t type_part ) -> proto_field::Type
     242              : {
     243          983 :     if( const auto type = resolve_enum( message, field, type_part ); type )
     244          133 :         return type;
     245              : 
     246          850 :     if( const auto * sub_message = get_sub_message( message, field, type_part ); sub_message )
     247              :     {
     248          406 :         if( is_last_part( field, type_part ) )
     249          369 :             return proto_field::Type::MESSAGE;
     250              : 
     251           37 :         return resolve_from_message( *sub_message, field, type_part + 1 );
     252              :     }
     253              : 
     254          444 :     return proto_field::Type::NONE;
     255              : }
     256              : 
     257          444 : [[nodiscard]] auto resolve_from_parent( const search_ctx & self, const proto_field & field,
     258              :                                         size_t type_part ) -> proto_field::Type
     259              : {
     260          444 :     if( !self.p_parent || type_part > 0 )
     261            2 :         return proto_field::Type::NONE;
     262              : 
     263          442 :     return resolve_type( *self.p_parent, field, type_part ).first;
     264              : }
     265              : 
     266            2 : [[nodiscard]] auto resolve_from_import( const proto_file & import, const proto_field & field )
     267              :     -> proto_field::Type
     268              : {
     269            2 :     if( field.type_name.size( ) > import.package.name.size( ) &&
     270            4 :         field.type_name[ import.package.name.size( ) ] == '.' &&
     271            2 :         field.type_name.starts_with( import.package.name ) )
     272              :     {
     273            2 :         return resolve_from_message( import.package, field, type_parts( import.package.name ) + 1 );
     274              :     }
     275              : 
     276            0 :     return proto_field::Type::NONE;
     277              : }
     278              : 
     279            2 : [[nodiscard]] auto resolve_from_imports( const search_ctx & self, const proto_field & field,
     280              :                                          size_t type_part ) -> proto_field::Type
     281              : {
     282            2 :     if( type_part > 0 )
     283            0 :         return proto_field::Type::NONE;
     284              : 
     285            2 :     for( const auto & import : self.file.file_imports )
     286              :     {
     287            2 :         if( const auto type = resolve_from_import( import, field ); type )
     288            2 :             return type;
     289              :     }
     290              : 
     291            0 :     return proto_field::Type::NONE;
     292              : }
     293              : 
     294         1769 : auto resolve_type( const search_ctx & self, const proto_field & field, size_t type_part )
     295              :     -> std::pair< proto_field::Type, proto_field::BitType >
     296              : {
     297         1769 :     if( const auto type = get_scalar_type( self.file, field, type_part ); type.first )
     298          825 :         return type;
     299              : 
     300          944 :     if( const auto type = resolve_from_message( self.message, field, type_part ); type )
     301          500 :         return { type, proto_field::BitType::NONE };
     302              : 
     303          444 :     if( const auto type = resolve_from_parent( self, field, type_part ); type )
     304          442 :         return { type, proto_field::BitType::NONE };
     305              : 
     306            2 :     if( const auto type = resolve_from_imports( self, field, type_part ); type )
     307            2 :         return { type, proto_field::BitType::NONE };
     308              : 
     309            0 :     throw_parse_error( self.file, field.type_name, "type can't be resolved" );
     310              : }
     311              : 
     312         1327 : void resolve_types( const search_ctx & self, proto_field & field )
     313              : {
     314         1327 :     const auto [ type, bit_type ] = resolve_type( self, field, 0 );
     315              : 
     316         1327 :     field.type     = type;
     317         1327 :     field.bit_type = bit_type;
     318         1327 : }
     319              : 
     320           52 : void resolve_types( const search_ctx & self, proto_map & map )
     321              : {
     322           52 :     resolve_types( self, map.key );
     323           52 :     resolve_types( self, map.value );
     324           52 : }
     325              : 
     326            4 : void resolve_types( const search_ctx & self, proto_oneof & oneof )
     327              : {
     328           15 :     for( auto & field : oneof.fields )
     329              :     {
     330           11 :         resolve_types( self, field );
     331              :     }
     332            4 : }
     333              : 
     334          466 : void resolve_types( const search_ctx & parent, proto_message & message )
     335              : {
     336              :     auto ctx = search_ctx{
     337          466 :         .file     = parent.file,
     338              :         .message  = message,
     339              :         .p_parent = &parent,
     340          466 :     };
     341              : 
     342         1678 :     for( auto & field : message.fields )
     343              :     {
     344         1212 :         resolve_types( ctx, field );
     345              :     }
     346              : 
     347          518 :     for( auto & map : message.maps )
     348              :     {
     349           52 :         resolve_types( ctx, map );
     350              :     }
     351              : 
     352          470 :     for( auto & oneof : message.oneofs )
     353              :     {
     354            4 :         resolve_types( ctx, oneof );
     355              :     }
     356              : 
     357          918 :     for( auto & sub_message : message.messages )
     358              :     {
     359          452 :         resolve_types( ctx, sub_message );
     360              :     }
     361          466 : }
     362              : }// namespace
     363              : 
     364         1448 : auto is_packed_array( const proto_file & file, const proto_field & field ) -> bool
     365              : {
     366         1448 :     if( field.label != proto_field::Label::REPEATED )
     367         1210 :         return { };
     368              : 
     369          238 :     if( file.syntax.version < 3 )
     370              :     {
     371          150 :         const auto p_packed = field.options.find( "packed" );
     372          150 :         return p_packed != field.options.end( ) && p_packed->second == "true";
     373              :     }
     374              :     else
     375              :     {
     376           88 :         const auto p_packed = field.options.find( "packed" );
     377           88 :         return p_packed == field.options.end( ) || p_packed->second != "false";
     378              :     }
     379              : }
     380              : 
     381         1936 : auto is_scalar( const proto_field::Type & type ) -> bool
     382              : {
     383         1936 :     switch( type )
     384              :     {
     385          906 :     case proto_field::Type::NONE:
     386              :     case proto_field::Type::MESSAGE:
     387              :     case proto_field::Type::ENUM:
     388          906 :         return false;
     389              : 
     390         1030 :     case proto_field::Type::BYTES:
     391              :     case proto_field::Type::STRING:
     392              :     case proto_field::Type::BOOL:
     393              :     case proto_field::Type::INT32:
     394              :     case proto_field::Type::UINT32:
     395              :     case proto_field::Type::INT64:
     396              :     case proto_field::Type::UINT64:
     397              :     case proto_field::Type::SINT32:
     398              :     case proto_field::Type::SINT64:
     399              :     case proto_field::Type::FLOAT:
     400              :     case proto_field::Type::FIXED32:
     401              :     case proto_field::Type::SFIXED32:
     402              :     case proto_field::Type::DOUBLE:
     403              :     case proto_field::Type::FIXED64:
     404              :     case proto_field::Type::SFIXED64:
     405         1030 :         return true;
     406              :     }
     407              : 
     408            0 :     return false;
     409              : }
     410              : 
     411           14 : void resolve_types( proto_file & file )
     412              : {
     413           14 :     auto ctx = search_ctx{
     414              :         .file     = file,
     415           14 :         .message  = file.package,
     416              :         .p_parent = nullptr,
     417           14 :     };
     418           14 :     resolve_types( ctx, file.package );
     419           14 : }
        

Generated by: LCOV version 2.0-1