ffi-clang 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/ext/rakefile.rb +2 -2
  4. data/lib/ffi/clang/clang_version.rb +7 -3
  5. data/lib/ffi/clang/code_completion.rb +121 -44
  6. data/lib/ffi/clang/comment.rb +164 -57
  7. data/lib/ffi/clang/compilation_database.rb +79 -25
  8. data/lib/ffi/clang/cursor.rb +395 -149
  9. data/lib/ffi/clang/diagnostic.rb +57 -23
  10. data/lib/ffi/clang/error.rb +12 -0
  11. data/lib/ffi/clang/file.rb +30 -11
  12. data/lib/ffi/clang/index.rb +37 -13
  13. data/lib/ffi/clang/lib/clang_version.rb +2 -2
  14. data/lib/ffi/clang/lib/code_completion.rb +15 -11
  15. data/lib/ffi/clang/lib/comment.rb +16 -14
  16. data/lib/ffi/clang/lib/compilation_database.rb +5 -5
  17. data/lib/ffi/clang/lib/cursor.rb +74 -56
  18. data/lib/ffi/clang/lib/diagnostic.rb +14 -14
  19. data/lib/ffi/clang/lib/file.rb +10 -6
  20. data/lib/ffi/clang/lib/inclusions.rb +3 -3
  21. data/lib/ffi/clang/lib/index.rb +7 -5
  22. data/lib/ffi/clang/lib/printing_policy.rb +36 -36
  23. data/lib/ffi/clang/lib/source_location.rb +9 -7
  24. data/lib/ffi/clang/lib/source_range.rb +5 -3
  25. data/lib/ffi/clang/lib/string.rb +9 -4
  26. data/lib/ffi/clang/lib/token.rb +17 -4
  27. data/lib/ffi/clang/lib/translation_unit.rb +17 -13
  28. data/lib/ffi/clang/lib/type.rb +19 -17
  29. data/lib/ffi/clang/lib.rb +35 -19
  30. data/lib/ffi/clang/platform.rb +25 -0
  31. data/lib/ffi/clang/printing_policy.rb +31 -18
  32. data/lib/ffi/clang/source_location.rb +119 -36
  33. data/lib/ffi/clang/source_range.rb +30 -12
  34. data/lib/ffi/clang/token.rb +48 -23
  35. data/lib/ffi/clang/translation_unit.rb +97 -33
  36. data/lib/ffi/clang/types/array.rb +15 -1
  37. data/lib/ffi/clang/types/elaborated.rb +19 -4
  38. data/lib/ffi/clang/types/function.rb +35 -10
  39. data/lib/ffi/clang/types/pointer.rb +23 -7
  40. data/lib/ffi/clang/types/record.rb +23 -8
  41. data/lib/ffi/clang/types/type.rb +80 -36
  42. data/lib/ffi/clang/types/type_def.rb +14 -2
  43. data/lib/ffi/clang/types/vector.rb +13 -1
  44. data/lib/ffi/clang/unsaved_file.rb +18 -8
  45. data/lib/ffi/clang/version.rb +4 -2
  46. data/lib/ffi/clang.rb +23 -45
  47. data/license.md +3 -2
  48. data/readme.md +12 -13
  49. data/releases.md +5 -0
  50. data.tar.gz.sig +0 -0
  51. metadata +10 -5
  52. metadata.gz.sig +0 -0
@@ -3,68 +3,94 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2010, by Jari Bakken.
5
5
  # Copyright, 2012, by Hal Brodigan.
6
- # Copyright, 2013-2022, by Samuel Williams.
6
+ # Copyright, 2013-2025, by Samuel Williams.
7
7
  # Copyright, 2013, by Garry Marshall.
8
8
  # Copyright, 2014, by Masahiro Sano.
9
9
  # Copyright, 2014, by Greg Hazel.
10
10
  # Copyright, 2019, by Michael Metivier.
11
11
  # Copyright, 2022, by Motonori Iwamuro.
12
12
 
13
- require_relative 'lib/translation_unit'
14
- require_relative 'lib/inclusions'
15
- require_relative 'cursor'
16
- require_relative 'file'
17
- require_relative 'token'
13
+ require_relative "lib/translation_unit"
14
+ require_relative "lib/inclusions"
15
+ require_relative "cursor"
16
+ require_relative "file"
17
+ require_relative "token"
18
+ require_relative "error"
18
19
 
19
20
  module FFI
20
21
  module Clang
22
+ # Represents a single translation unit (a compiled source file with its dependencies).
21
23
  class TranslationUnit < AutoPointer
24
+ # Initialize a translation unit with a pointer and parent index.
25
+ # @parameter pointer [FFI::Pointer] The translation unit pointer.
26
+ # @parameter index [Index] The parent index that created this translation unit.
22
27
  def initialize(pointer, index)
23
28
  super pointer
24
29
  @index = index
25
30
  end
26
-
31
+
32
+ # Release the translation unit pointer.
33
+ # @parameter pointer [FFI::Pointer] The translation unit pointer to release.
27
34
  def self.release(pointer)
28
35
  Lib.dispose_translation_unit(pointer)
29
36
  end
30
-
37
+
38
+ # Get the default editing translation unit options.
39
+ # @returns [Array(Symbol)] The default editing options.
31
40
  def self.default_editing_translation_unit_options
32
41
  bitmask = Lib.default_editing_translation_unit_options
33
42
  Lib.opts_from Lib::TranslationUnitFlags, bitmask
34
43
  end
35
-
44
+
45
+ # Get the default save options for this translation unit.
46
+ # @returns [Array(Symbol)] The default save options.
36
47
  def default_save_options
37
48
  bitmask = Lib.default_save_options(self)
38
49
  Lib.opts_from Lib::SaveTranslationUnitFlags, bitmask
39
50
  end
40
-
51
+
52
+ # Save the translation unit to a file.
53
+ # @parameter filename [String] The path where the translation unit should be saved.
54
+ # @parameter opts [Hash] Save options.
55
+ # @raises [Error] If saving fails.
41
56
  def save(filename, opts = {})
42
57
  ret = Lib.save_translation_unit(self, filename, 0)
43
58
  sym = Lib::SaveError[ret]
44
59
  raise Error, "unknown return values: #{ret} #{sym.inspect}" unless sym
45
60
  raise Error, "save error: #{sym.inspect}, filename: #{filename}" if sym != :none
46
61
  end
47
-
62
+
63
+ # Get the default reparse options for this translation unit.
64
+ # @returns [Array(Symbol)] The default reparse options.
48
65
  def default_reparse_options
49
66
  bitmask = Lib.default_save_options(self)
50
67
  Lib.opts_from Lib::ReparseFlags, bitmask
51
68
  end
52
-
69
+
70
+ # Reparse the translation unit with updated file contents.
71
+ # @parameter unsaved [Array(UnsavedFile)] Unsaved file buffers.
72
+ # @parameter opts [Hash] Reparse options.
73
+ # @raises [Error] If reparsing fails.
53
74
  def reparse(unsaved = [], opts = {})
54
75
  unsaved_files = UnsavedFile.unsaved_pointer_from(unsaved)
55
76
  if Lib.reparse_translation_unit(self, unsaved.size, unsaved_files, 0) != 0
56
77
  raise Error, "reparse error"
57
78
  end
58
79
  end
59
-
80
+
81
+ # Get all diagnostics for this translation unit.
82
+ # @returns [Array(Diagnostic)] Array of diagnostics.
60
83
  def diagnostics
61
84
  n = Lib.get_num_diagnostics(self)
62
-
85
+
63
86
  n.times.map do |i|
64
87
  Diagnostic.new(self, Lib.get_diagnostic(self, i))
65
88
  end
66
89
  end
67
-
90
+
91
+ # Get a cursor for the translation unit or at a specific location.
92
+ # @parameter location [SourceLocation | Nil] The location for the cursor, or `nil` for the root cursor.
93
+ # @returns [Cursor] The cursor at the specified location or the root cursor.
68
94
  def cursor(location = nil)
69
95
  if location.nil?
70
96
  Cursor.new Lib.get_translation_unit_cursor(self), self
@@ -72,15 +98,27 @@ module FFI
72
98
  Cursor.new Lib.get_cursor(self, location.location), self
73
99
  end
74
100
  end
75
-
101
+
102
+ # Get a source location by file, line, and column.
103
+ # @parameter file [File] The file object.
104
+ # @parameter line [Integer] The line number (1-indexed).
105
+ # @parameter column [Integer] The column number (1-indexed).
106
+ # @returns [ExpansionLocation] The source location.
76
107
  def location(file, line, column)
77
108
  ExpansionLocation.new Lib.get_location(self, file, line, column)
78
109
  end
79
-
110
+
111
+ # Get a source location by file and byte offset.
112
+ # @parameter file [File] The file object.
113
+ # @parameter offset [Integer] The byte offset from the start of the file.
114
+ # @returns [ExpansionLocation] The source location.
80
115
  def location_offset(file, offset)
81
116
  ExpansionLocation.new Lib.get_location_offset(self, file, offset)
82
117
  end
83
-
118
+
119
+ # Get a file object from this translation unit.
120
+ # @parameter file_name [String | Nil] The file name, or `nil` to get the main file.
121
+ # @returns [File] The file object.
84
122
  def file(file_name = nil)
85
123
  if file_name.nil?
86
124
  File.new(Lib.get_file(self, spelling), self)
@@ -88,22 +126,36 @@ module FFI
88
126
  File.new(Lib.get_file(self, file_name), self)
89
127
  end
90
128
  end
91
-
129
+
130
+ # Get the spelling (filename) of this translation unit.
131
+ # @returns [String] The filename of the translation unit.
92
132
  def spelling
93
133
  Lib.extract_string Lib.get_translation_unit_spelling(self)
94
134
  end
95
-
135
+
136
+ # Get resource usage information for this translation unit.
137
+ # @returns [ResourceUsage] Resource usage statistics.
96
138
  def resource_usage
97
139
  FFI::Clang::TranslationUnit::ResourceUsage.new Lib.resource_usage(self)
98
140
  end
99
-
141
+
142
+ # Tokenize a source range.
143
+ # @parameter range [SourceRange] The source range to tokenize.
144
+ # @returns [Tokens] Collection of tokens in the range.
100
145
  def tokenize(range)
101
146
  token_ptr = MemoryPointer.new :pointer
102
147
  uint_ptr = MemoryPointer.new :uint
103
148
  Lib.tokenize(self, range.range, token_ptr, uint_ptr)
104
149
  Tokens.new(token_ptr.get_pointer(0), uint_ptr.get_uint(0), self)
105
150
  end
106
-
151
+
152
+ # Perform code completion at a specific location.
153
+ # @parameter source_file [String] The path to the source file.
154
+ # @parameter line [Integer] The line number for code completion.
155
+ # @parameter column [Integer] The column number for code completion.
156
+ # @parameter unsaved [Array(UnsavedFile)] Unsaved file buffers.
157
+ # @parameter opts [Array(Symbol) | Nil] Code completion options, or `nil` for defaults.
158
+ # @returns [CodeCompletion::Results] The code completion results.
107
159
  def code_complete(source_file, line, column, unsaved = [], opts = nil)
108
160
  opts = CodeCompletion.default_code_completion_options if opts.nil?
109
161
  unsaved_files = UnsavedFile.unsaved_pointer_from(unsaved)
@@ -111,23 +163,29 @@ module FFI
111
163
  ptr = Lib.code_complete_at(self, source_file, line, column, unsaved_files, unsaved.length, option_bitmask)
112
164
  CodeCompletion::Results.new ptr, self
113
165
  end
114
-
166
+
167
+ # Iterate over all file inclusions in this translation unit.
168
+ # @yields {|file, locations| ...} Each inclusion with its file path and stack.
169
+ # @parameter file [String] The included file path.
170
+ # @parameter locations [Array(SourceLocation)] The inclusion stack.
115
171
  def inclusions(&block)
116
172
  adapter = Proc.new do |included_file, inclusion_stack, include_len, unused|
117
173
  file = Lib.extract_string Lib.get_file_name(included_file)
118
174
  cur_ptr = inclusion_stack
119
175
  inclusions = []
120
- include_len.times {
121
- inclusions << SourceLocation.new(Lib::CXSourceLocation.new(cur_ptr))
122
- cur_ptr += Lib::CXSourceLocation.size
176
+ include_len.times {inclusions << SourceLocation.new(Lib::CXSourceLocation.new(cur_ptr))
177
+ cur_ptr += Lib::CXSourceLocation.size
123
178
  }
124
179
  block.call file, inclusions
125
180
  end
126
181
 
127
182
  Lib.get_inclusions(self, adapter, nil)
128
183
  end
129
-
184
+
185
+ # Represents resource usage statistics for a translation unit.
130
186
  class ResourceUsage < AutoPointer
187
+ # Initialize resource usage from a CXTUResourceUsage structure.
188
+ # @parameter resource_usage [Lib::CXTUResourceUsage] The resource usage structure.
131
189
  def initialize(resource_usage)
132
190
  # CXResourceUsage is returned by value and should be freed explicitly.
133
191
  # Get FFI::pointer of the data so that the data is handled by AutoPointer.
@@ -135,22 +193,28 @@ module FFI
135
193
  super(pointer)
136
194
  @resource_usage = resource_usage
137
195
  end
138
-
196
+
197
+ # Release the resource usage pointer.
198
+ # @parameter pointer [FFI::Pointer] The resource usage pointer to release.
139
199
  def self.release(pointer)
140
200
  # clang_disposeCXTUResourceUsage requires value type, so create it by pointer
141
201
  Lib.dispose_resource_usage(Lib::CXTUResourceUsage.new(pointer))
142
202
  end
143
-
203
+
204
+ # Get the name of a resource usage kind.
205
+ # @parameter kind [Symbol] The resource usage kind.
206
+ # @returns [String] The name of the resource kind.
144
207
  def self.name(kind)
145
208
  Lib.resource_usage_name(kind)
146
209
  end
147
-
210
+
211
+ # Get all resource usage entries.
212
+ # @returns [Array(Lib::CXTUResourceUsageEntry)] The resource usage entries.
148
213
  def entries
149
214
  ary = []
150
215
  ptr = @resource_usage[:entries]
151
- @resource_usage[:numEntries].times {
152
- ary << Lib::CXTUResourceUsageEntry.new(ptr)
153
- ptr += Lib::CXTUResourceUsageEntry.size
216
+ @resource_usage[:numEntries].times {ary << Lib::CXTUResourceUsageEntry.new(ptr)
217
+ ptr += Lib::CXTUResourceUsageEntry.size
154
218
  }
155
219
  ary
156
220
  end
@@ -1,11 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Charlie Savage.
5
+ # Copyright, 2025, by Samuel Williams.
6
+
1
7
  module FFI
2
8
  module Clang
9
+ # Type system classes for representing C/C++ types.
10
+ # @namespace
3
11
  module Types
12
+ # Represents an array type.
13
+ # This includes constant arrays, incomplete arrays, variable arrays, and dependent-sized arrays.
4
14
  class Array < Type
15
+ # Get the element type of this array.
16
+ # @returns [Type] The type of elements in this array.
5
17
  def element_type
6
18
  Type.create Lib.get_array_element_type(@type), @translation_unit
7
19
  end
8
-
20
+
21
+ # Get the size of this array.
22
+ # @returns [Integer] The number of elements in the array, or -1 if the size is not available.
9
23
  def size
10
24
  Lib.get_array_size(@type)
11
25
  end
@@ -1,26 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024-2025, by Charlie Savage.
5
+ # Copyright, 2025, by Samuel Williams.
6
+
1
7
  module FFI
2
8
  module Clang
3
9
  module Types
10
+ # Represents an elaborated type (e.g., struct, union, enum with an elaborated type specifier).
11
+ # Elaborated types may include type qualifiers and nested name specifiers.
4
12
  class Elaborated < Type
13
+ # Get the named type that this elaborated type refers to.
14
+ # @returns [Type] The underlying named type.
5
15
  def named_type
6
16
  Type.create Lib.get_named_type(@type), @translation_unit
7
17
  end
8
-
9
- # Example anonymous union where `u` is an elaborated type
18
+
19
+ # Check if this is an anonymous elaborated type.
20
+ # Example anonymous union where `u` is an elaborated type:
10
21
  #
11
22
  # typedef struct {
12
23
  # union {
13
24
  # int idata;
14
25
  # } u;
15
26
  # } SomeStruct;
27
+ #
28
+ # @returns [Boolean] True if this elaborated type is anonymous.
16
29
  def anonymous?
17
30
  self.declaration.anonymous?
18
31
  end
19
-
32
+
33
+ # Check if this elaborated type is a pointer in its canonical form.
34
+ # @returns [Boolean] True if the canonical type is a pointer.
20
35
  def pointer?
21
36
  self.canonical.is_a?(Pointer)
22
37
  end
23
38
  end
24
39
  end
25
40
  end
26
- end
41
+ end
@@ -1,43 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Charlie Savage.
5
+ # Copyright, 2025, by Samuel Williams.
6
+
1
7
  module FFI
2
8
  module Clang
3
9
  module Types
10
+ # Represents a function type.
11
+ # This includes functions with and without prototypes.
4
12
  class Function < Type
5
13
  include Enumerable
6
-
14
+
15
+ # Check if this function is variadic.
16
+ # @returns [Boolean] True if the function accepts a variable number of arguments.
7
17
  def variadic?
8
18
  Lib.is_function_type_variadic(@type) != 0
9
19
  end
10
-
20
+
21
+ # Get the number of arguments this function takes.
22
+ # @returns [Integer] The number of arguments.
11
23
  def args_size
12
24
  Lib.get_num_arg_types(@type)
13
25
  end
14
-
26
+
27
+ # Get the type of a specific argument.
28
+ # @parameter i [Integer] The zero-based argument index.
29
+ # @returns [Type] The type of the argument at the specified index.
15
30
  def arg_type(i)
16
31
  Type.create Lib.get_arg_type(@type, i), @translation_unit
17
32
  end
18
-
33
+
34
+ # Iterate over all argument types or get an enumerator.
35
+ # @yields {|type| ...} Yields each argument type if a block is given.
36
+ # @parameter type [Type] An argument type.
37
+ # @returns [Enumerator, self] An enumerator if no block is given, self otherwise.
19
38
  def arg_types
20
39
  return to_enum(:arg_types) unless block_given?
21
-
40
+
22
41
  self.args_size.times do |i|
23
42
  yield self.arg_type(i)
24
43
  end
25
-
44
+
26
45
  self
27
46
  end
28
-
47
+
48
+ # Get the return type of this function.
49
+ # @returns [Type] The function's return type.
29
50
  def result_type
30
51
  Type.create Lib.get_result_type(@type), @translation_unit
31
52
  end
32
-
53
+
54
+ # Get the calling convention of this function.
55
+ # @returns [Symbol] The calling convention (e.g., :calling_conv_c, :calling_conv_x86_stdcall).
33
56
  def calling_conv
34
57
  Lib.get_fuction_type_calling_conv(@type)
35
58
  end
36
-
59
+
60
+ # Get the exception specification type for this function.
61
+ # @returns [Symbol] The exception specification type (e.g., :exception_spec_none, :exception_spec_basic_noexcept).
37
62
  def exception_specification
38
63
  Lib.get_exception_specification_type(@type)
39
64
  end
40
65
  end
41
66
  end
42
67
  end
43
- end
68
+ end
@@ -1,15 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Charlie Savage.
5
+ # Copyright, 2025, by Samuel Williams.
6
+
1
7
  module FFI
2
8
  module Clang
3
9
  module Types
10
+ # Represents a pointer type.
11
+ # This includes regular pointers, block pointers, Objective-C object pointers, and member pointers.
4
12
  class Pointer < Type
13
+ # Get the type that this pointer points to.
14
+ # @returns [Type] The pointee type.
5
15
  def pointee
6
16
  Type.create Lib.get_pointee_type(@type), @translation_unit
7
17
  end
8
-
18
+
19
+ # Check if this is a function pointer.
20
+ # @returns [Boolean] True if this pointer points to a function.
9
21
  def function?
10
22
  self.pointee.is_a?(Types::Function)
11
23
  end
12
-
24
+
25
+ # Get the class type for member pointers.
26
+ # @returns [Type, nil] The class type if this is a member pointer, nil otherwise.
13
27
  def class_type
14
28
  if self.kind == :type_member_pointer
15
29
  Type.create Lib.type_get_class_type(@type), @translation_unit
@@ -17,16 +31,18 @@ module FFI
17
31
  nil
18
32
  end
19
33
  end
20
-
34
+
35
+ # Check if this pointer references a forward declaration.
36
+ # @returns [Boolean] True if this pointer points to a forward-declared type.
21
37
  def forward_declaration?
22
38
  # Is this a pointer to a record (struct or union) that referenced
23
39
  # a forward declaration at the point of its inclusion in the translation unit?
24
40
  if !self.function? && self.pointee.is_a?(Types::Elaborated) &&
25
- self.pointee.canonical.is_a?(Types::Record)
26
-
41
+ self.pointee.canonical.is_a?(Types::Record)
42
+
27
43
  # Get the universal symbol reference
28
44
  usr = self.pointee.canonical.declaration.usr
29
-
45
+
30
46
  # Now does that same usr occur earlier in the file?
31
47
  first_declaration, _ = self.translation_unit.cursor.find do |child, parent|
32
48
  child.usr == usr
@@ -39,4 +55,4 @@ module FFI
39
55
  end
40
56
  end
41
57
  end
42
- end
58
+ end
@@ -1,23 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Charlie Savage.
5
+ # Copyright, 2025, by Samuel Williams.
6
+
1
7
  module FFI
2
8
  module Clang
3
9
  module Types
10
+ # Represents a record type (struct or union).
4
11
  class Record < Type
12
+ # Get the byte offset of a field within this record.
13
+ # @parameter field [String] The name of the field.
14
+ # @returns [Integer] The byte offset of the field, or -1 if not found.
5
15
  def offsetof(field)
6
16
  Lib.type_get_offset_of(@type, field)
7
17
  end
8
-
18
+
19
+ # Check if this is an anonymous record.
20
+ # @returns [Boolean] True if this record is unnamed/anonymous.
9
21
  def anonymous?
10
22
  self.spelling.match(/unnamed/)
11
23
  end
12
-
24
+
25
+ # Get the kind of record (struct or union).
26
+ # @returns [Symbol] Either :struct or :union.
27
+ # @raises [RuntimeError] If the record type cannot be determined.
13
28
  def record_type
14
29
  case self.spelling
15
- when /struct/
16
- :struct
17
- when /union/
18
- :union
19
- else
20
- raise("Unknown record type: #{self.spelling}")
30
+ when /struct/
31
+ :struct
32
+ when /union/
33
+ :union
34
+ else
35
+ raise("Unknown record type: #{self.spelling}")
21
36
  end
22
37
  end
23
38
  end