@@ -180,6 +180,7 @@ def initialize(inputter, outputter, config)
180180 @incomplete_iterators = Hash . new
181181 # Iterator names per class (for aliasing each_const -> each)
182182 @class_iterator_names = Hash . new { |h , k | h [ k ] = Set . new }
183+ @project_complete_types = Set . new
183184 end
184185
185186 # Parse the configured inputs with libclang and stream the resulting
@@ -188,6 +189,7 @@ def generate
188189 clang_args = @config [ :clang_args ] || [ ]
189190 parser = RubyBindgen ::Parser . new ( @inputter , clang_args , libclang : @config [ :libclang ] )
190191 ::FFI ::Clang ::Cursor . namer = @namer
192+ build_project_complete_type_index ( parser )
191193 parser . generate ( self )
192194 end
193195
@@ -291,6 +293,7 @@ def unsupported_rice_opaque_namespace_type?(type)
291293 decl = type . canonical . declaration
292294 return false if decl . kind == :cursor_no_decl_found
293295 return false unless decl . opaque_declaration?
296+ return false if project_complete_type? ( decl )
294297 return false if decl . qualified_name &.start_with? ( "std::" , "__gnu_cxx::" )
295298 return false if [ :cursor_class_decl , :cursor_struct ] . include? ( decl . semantic_parent . kind )
296299
@@ -449,7 +452,6 @@ def visit_translation_unit(translation_unit, path, relative_path)
449452 @non_member_operators . clear
450453 @incomplete_iterators . clear
451454 @class_iterator_names . clear
452- @declared_function_qualified_names = nil
453455 cursor = translation_unit . cursor
454456 @translation_unit_cursor = cursor
455457 @type_speller . printing_policy = cursor . printing_policy
@@ -516,6 +518,40 @@ def visit_translation_unit(translation_unit, path, relative_path)
516518 self . outputter . write ( rice_header , content )
517519 end
518520
521+ def build_project_complete_type_index ( parser )
522+ @project_complete_types . clear
523+
524+ @inputter . each do |path , _relative_path |
525+ begin
526+ translation_unit = parser . send ( :parse_translation_unit , path )
527+ rescue RubyBindgen ::Parser ::ParseError
528+ next
529+ end
530+
531+ record_project_complete_types ( translation_unit . cursor )
532+ end
533+ end
534+
535+ def record_project_complete_types ( cursor )
536+ cursor . find_by_kind ( true , :cursor_class_decl , :cursor_struct ) do |child |
537+ next if child . spelling . empty?
538+ next if child . opaque_declaration?
539+ next unless translation_unit_file? ( child )
540+
541+ qualified_name = child . qualified_name
542+ next if qualified_name . nil? || qualified_name . empty?
543+
544+ @project_complete_types << qualified_name
545+ end
546+ end
547+
548+ def project_complete_type? ( decl )
549+ qualified_name = decl . qualified_name
550+ return false if qualified_name . nil? || qualified_name . empty?
551+
552+ @project_complete_types . include? ( qualified_name )
553+ end
554+
519555 # Render a public, callable constructor into the Rice chain for its class.
520556 def visit_constructor ( cursor )
521557 # Do not process class constructors defined outside of the class definition
@@ -553,16 +589,7 @@ def visit_constructor(cursor)
553589 # constants, embedded types, and any auto-generated template bases needed
554590 # before the class itself can be registered.
555591 def visit_class_decl ( cursor )
556- # Namespace-scope forward-declared C++ classes are often completed in a
557- # different header. Emitting a Rice class here creates a Ruby constant
558- # with no superclass, and a later complete definition then conflicts.
559- # Keep nested incomplete classes on the existing special path, and keep
560- # opaque structs available for handle-style APIs.
561- if cursor . kind == :cursor_class_decl &&
562- cursor . opaque_declaration? &&
563- ![ :cursor_class_decl , :cursor_struct ] . include? ( cursor . semantic_parent . kind )
564- return
565- end
592+ return if skip_namespace_forward_declaration? ( cursor )
566593
567594 # Skip explicitly listed symbols
568595 return if skip_symbol? ( cursor )
@@ -896,28 +923,12 @@ def skip_callable?(cursor)
896923 cursor . type . variadic?
897924 end
898925
899- def unresolved_inline_calls? ( cursor )
900- source = inline_definition_source ( cursor )
901- return false unless source &.include? ( '{' )
902-
903- function_calls = source . scan ( /([A-Za-z_]\w *(?:::[A-Za-z_]\w *)+)\s *\( / )
904- . flatten
905- . uniq
906- . select { |name | name . split ( '::' ) . last . match? ( /\A [a-z_]\w *\z / ) }
907- return false if function_calls . empty?
908-
909- function_calls . any? do |qualified_name |
910- !declared_function_qualified_names . include? ( qualified_name )
911- end
912- end
913-
914926 # Render a class method, including special handling for iterator adapters
915927 # and mutable `operator[]` setter support.
916928 def visit_cxx_method ( cursor )
917929 # Do not process method definitions outside of classes (because we already processed them)
918930 return if cursor . lexical_parent != cursor . semantic_parent
919931 return if skip_callable? ( cursor )
920- return if unresolved_inline_calls? ( cursor )
921932 return if has_skipped_param_type? ( cursor )
922933 return if has_unsupported_rice_param_type? ( cursor )
923934 return if has_skipped_return_type? ( cursor )
@@ -965,43 +976,16 @@ def visit_cxx_method(cursor)
965976 result
966977 end
967978
968- def declared_function_qualified_names
969- @declared_function_qualified_names ||= @translation_unit_cursor
970- . find_by_kind ( true , :cursor_function , :cursor_function_template )
971- . map ( &:qualified_name )
972- . reject ( &:empty? )
973- . to_set
974- end
979+ def skip_namespace_forward_declaration? ( cursor )
980+ return false unless cursor . kind == :cursor_class_decl
981+ return false unless cursor . opaque_declaration?
982+ return false if [ :cursor_class_decl , :cursor_struct ] . include? ( cursor . semantic_parent . kind )
975983
976- def inline_definition_source ( cursor )
977- parent = cursor . semantic_parent
978- source = parent &.extent &.text
979- return nil if source . nil? || source . empty?
980-
981- line_index = cursor . extent . start . line - parent . extent . start . line
982- lines = source . lines
983- return nil if line_index . negative? || line_index >= lines . length
984-
985- result = String . new
986- brace_depth = 0
987- saw_body = false
988-
989- lines [ line_index ..] . each do |line |
990- result << line
991- line . each_char do |char |
992- if char == '{'
993- saw_body = true
994- brace_depth += 1
995- elsif char == '}'
996- brace_depth -= 1 if brace_depth . positive?
997- end
998- end
999-
1000- break if saw_body && brace_depth . zero?
1001- break if !saw_body && line . include? ( ';' )
1002- end
984+ definition = cursor . definition
985+ return false if [ :cursor_invalid_file , :cursor_no_decl_found ] . include? ( definition . kind )
986+ return false if definition . opaque_declaration?
1003987
1004- result
988+ translation_unit_file? ( definition )
1005989 end
1006990
1007991 # Check if an iterator type has proper std::iterator_traits.
0 commit comments