rubydex 0.2.0 → 0.2.2

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.
@@ -4926,6 +4926,25 @@ mod visibility_resolution_tests {
4926
4926
  };
4927
4927
  }
4928
4928
 
4929
+ #[test]
4930
+ fn retroactive_visibility_override_applies_in_source_order() {
4931
+ let mut context = GraphTest::new();
4932
+ context.index_uri(
4933
+ "file:///foo.rb",
4934
+ r"
4935
+ class Foo
4936
+ def bar; end
4937
+ private :bar
4938
+ public :bar
4939
+ end
4940
+ ",
4941
+ );
4942
+ context.resolve();
4943
+
4944
+ assert_no_diagnostics!(&context);
4945
+ assert_visibility_eq!(context, "Foo#bar()", Visibility::Public);
4946
+ }
4947
+
4929
4948
  #[test]
4930
4949
  fn retroactive_visibility_on_direct_method() {
4931
4950
  let mut context = GraphTest::new();
@@ -5066,7 +5085,7 @@ mod visibility_resolution_tests {
5066
5085
  assert_diagnostics_eq!(
5067
5086
  context,
5068
5087
  &[
5069
- "undefined-method-visibility-target: undefined method `nonexistent()` for visibility change in `Foo` (2:12-2:23)"
5088
+ "undefined-method-visibility-target: undefined method `Foo#nonexistent()` for visibility change (2:12-2:23)"
5070
5089
  ]
5071
5090
  );
5072
5091
  }
@@ -5128,4 +5147,317 @@ mod visibility_resolution_tests {
5128
5147
  assert_owner_eq!(context, "Child#foo()", "Child");
5129
5148
  assert_visibility_eq!(context, "Child#foo()", Visibility::Private);
5130
5149
  }
5150
+
5151
+ #[test]
5152
+ fn retroactive_constant_visibility_on_direct_member() {
5153
+ let mut context = GraphTest::new();
5154
+ context.index_uri(
5155
+ "file:///foo.rb",
5156
+ r"
5157
+ class Foo
5158
+ BAR = 1
5159
+ private_constant :BAR
5160
+
5161
+ BAZ = 2
5162
+ public_constant :BAZ
5163
+
5164
+ QUX = 3
5165
+
5166
+ class Inner; end
5167
+ private_constant :Inner
5168
+
5169
+ module InnerMod; end
5170
+ private_constant :InnerMod
5171
+ end
5172
+ ",
5173
+ );
5174
+ context.resolve();
5175
+
5176
+ assert_no_diagnostics!(&context);
5177
+ assert_visibility_eq!(context, "Foo::BAR", Visibility::Private);
5178
+ assert_visibility_eq!(context, "Foo::BAZ", Visibility::Public);
5179
+ assert_visibility_eq!(context, "Foo::QUX", Visibility::Public);
5180
+ assert_visibility_eq!(context, "Foo::Inner", Visibility::Private);
5181
+ assert_visibility_eq!(context, "Foo::InnerMod", Visibility::Private);
5182
+ }
5183
+
5184
+ #[test]
5185
+ fn retroactive_constant_visibility_via_qualified_receiver() {
5186
+ let mut context = GraphTest::new();
5187
+ context.index_uri(
5188
+ "file:///foo.rb",
5189
+ r"
5190
+ class Foo
5191
+ BAR = 1
5192
+ BAZ = 2
5193
+ end
5194
+
5195
+ ALIAS = Foo
5196
+ Foo.private_constant :BAR
5197
+ ALIAS.private_constant :BAZ
5198
+ ",
5199
+ );
5200
+ context.resolve();
5201
+
5202
+ assert_no_diagnostics!(&context);
5203
+ assert_visibility_eq!(context, "Foo::BAR", Visibility::Private);
5204
+ assert_visibility_eq!(context, "Foo::BAZ", Visibility::Private);
5205
+ }
5206
+
5207
+ #[test]
5208
+ fn retroactive_constant_visibility_multi_arg_undefined_emits_per_name_diagnostic() {
5209
+ let mut context = GraphTest::new();
5210
+ context.index_uri(
5211
+ "file:///foo.rb",
5212
+ r"
5213
+ class Foo
5214
+ private_constant :NOPE_ONE, :NOPE_TWO
5215
+ end
5216
+ ",
5217
+ );
5218
+ context.resolve();
5219
+
5220
+ assert_diagnostics_eq!(
5221
+ context,
5222
+ &[
5223
+ "undefined-constant-visibility-target: undefined constant `NOPE_ONE` for visibility change in `Foo` (2:21-2:29)",
5224
+ "undefined-constant-visibility-target: undefined constant `NOPE_TWO` for visibility change in `Foo` (2:32-2:40)",
5225
+ ]
5226
+ );
5227
+ }
5228
+
5229
+ #[test]
5230
+ fn retroactive_constant_visibility_inherited_constant_emits_diagnostic() {
5231
+ let mut context = GraphTest::new();
5232
+ context.index_uri(
5233
+ "file:///foo.rb",
5234
+ r"
5235
+ class Parent
5236
+ CONST = 1
5237
+ end
5238
+
5239
+ class Child < Parent
5240
+ private_constant :CONST
5241
+ end
5242
+ ",
5243
+ );
5244
+ context.resolve();
5245
+
5246
+ assert_diagnostics_eq!(
5247
+ context,
5248
+ &[
5249
+ "undefined-constant-visibility-target: undefined constant `CONST` for visibility change in `Child` (6:21-6:26)"
5250
+ ]
5251
+ );
5252
+ assert_visibility_eq!(context, "Parent::CONST", Visibility::Public);
5253
+ }
5254
+
5255
+ #[test]
5256
+ fn retroactive_constant_visibility_clears_when_call_removed() {
5257
+ let mut context = GraphTest::new();
5258
+ context.index_uri(
5259
+ "file:///foo.rb",
5260
+ r"
5261
+ class Foo
5262
+ BAR = 1
5263
+ end
5264
+ ",
5265
+ );
5266
+ context.index_uri(
5267
+ "file:///vis.rb",
5268
+ r"
5269
+ Foo.private_constant :BAR
5270
+ ",
5271
+ );
5272
+ context.resolve();
5273
+
5274
+ assert_no_diagnostics!(&context);
5275
+ assert_visibility_eq!(context, "Foo::BAR", Visibility::Private);
5276
+
5277
+ context.delete_uri("file:///vis.rb");
5278
+ context.resolve();
5279
+
5280
+ assert_no_diagnostics!(&context);
5281
+ assert_visibility_eq!(context, "Foo::BAR", Visibility::Public);
5282
+ }
5283
+
5284
+ #[test]
5285
+ fn retroactive_constant_visibility_inside_singleton_class_body() {
5286
+ let mut context = GraphTest::new();
5287
+ context.index_uri(
5288
+ "file:///foo.rb",
5289
+ r"
5290
+ class Foo
5291
+ class << self
5292
+ BAR = 1
5293
+ private_constant :BAR
5294
+ end
5295
+ end
5296
+ ",
5297
+ );
5298
+ context.resolve();
5299
+
5300
+ assert_no_diagnostics!(&context);
5301
+ assert_visibility_eq!(context, "Foo::<Foo>::BAR", Visibility::Private);
5302
+ }
5303
+
5304
+ #[test]
5305
+ fn retroactive_constant_visibility_persists_across_reopened_class() {
5306
+ let mut context = GraphTest::new();
5307
+ context.index_uri(
5308
+ "file:///a.rb",
5309
+ r"
5310
+ class Foo
5311
+ BAR = 1
5312
+ private_constant :BAR
5313
+ end
5314
+ ",
5315
+ );
5316
+ context.index_uri(
5317
+ "file:///b.rb",
5318
+ r"
5319
+ class Foo
5320
+ BAR = 2
5321
+ end
5322
+ ",
5323
+ );
5324
+ context.resolve();
5325
+
5326
+ assert_visibility_eq!(context, "Foo::BAR", Visibility::Private);
5327
+ }
5328
+
5329
+ #[test]
5330
+ fn retroactive_singleton_method_visibility_on_direct_member() {
5331
+ let mut context = GraphTest::new();
5332
+ context.index_uri(
5333
+ "file:///foo.rb",
5334
+ r"
5335
+ class Foo
5336
+ def self.bar; end
5337
+ def self.baz; end
5338
+
5339
+ private_class_method :bar
5340
+ private_class_method :baz
5341
+ public_class_method :baz
5342
+ end
5343
+ ",
5344
+ );
5345
+ context.resolve();
5346
+
5347
+ assert_no_diagnostics!(&context);
5348
+ assert_visibility_eq!(context, "Foo::<Foo>#bar()", Visibility::Private);
5349
+ assert_visibility_eq!(context, "Foo::<Foo>#baz()", Visibility::Public);
5350
+ }
5351
+
5352
+ #[test]
5353
+ fn retroactive_singleton_method_visibility_on_inherited_method() {
5354
+ let mut context = GraphTest::new();
5355
+ context.index_uri(
5356
+ "file:///foo.rb",
5357
+ r"
5358
+ class Parent
5359
+ def self.foo; end
5360
+ end
5361
+
5362
+ class Child < Parent
5363
+ private_class_method :foo
5364
+ end
5365
+ ",
5366
+ );
5367
+ context.resolve();
5368
+
5369
+ assert_no_diagnostics!(&context);
5370
+ assert_declaration_exists!(context, "Child::<Child>#foo()");
5371
+ assert_visibility_eq!(context, "Child::<Child>#foo()", Visibility::Private);
5372
+ assert_visibility_eq!(context, "Parent::<Parent>#foo()", Visibility::Public);
5373
+ }
5374
+
5375
+ #[test]
5376
+ fn retroactive_singleton_method_visibility_on_undefined_method_emits_diagnostic() {
5377
+ let mut context = GraphTest::new();
5378
+ context.index_uri(
5379
+ "file:///foo.rb",
5380
+ r"
5381
+ class Foo
5382
+ private_class_method :nonexistent
5383
+ end
5384
+ ",
5385
+ );
5386
+ context.resolve();
5387
+
5388
+ assert_diagnostics_eq!(
5389
+ context,
5390
+ &[
5391
+ "undefined-method-visibility-target: undefined method `Foo::<Foo>#nonexistent()` for visibility change (2:25-2:36)"
5392
+ ]
5393
+ );
5394
+ }
5395
+
5396
+ #[test]
5397
+ fn retroactive_singleton_method_visibility_undefined_target_diagnostic_clears_when_file_deleted() {
5398
+ let mut context = GraphTest::new();
5399
+ context.index_uri(
5400
+ "file:///foo.rb",
5401
+ r"
5402
+ class Foo
5403
+ end
5404
+ ",
5405
+ );
5406
+ context.index_uri(
5407
+ "file:///bad.rb",
5408
+ r"
5409
+ class Foo
5410
+ private_class_method :missing
5411
+ end
5412
+ ",
5413
+ );
5414
+ context.resolve();
5415
+
5416
+ assert_diagnostics_eq!(
5417
+ context,
5418
+ &[
5419
+ "undefined-method-visibility-target: undefined method `Foo::<Foo>#missing()` for visibility change (2:25-2:32)"
5420
+ ]
5421
+ );
5422
+
5423
+ context.delete_uri("file:///bad.rb");
5424
+ context.resolve();
5425
+
5426
+ assert_no_diagnostics!(&context);
5427
+ }
5428
+
5429
+ #[test]
5430
+ fn retroactive_singleton_method_visibility_undefined_target_diagnostic_clears_when_target_added() {
5431
+ let mut context = GraphTest::new();
5432
+ context.index_uri(
5433
+ "file:///foo.rb",
5434
+ r"
5435
+ class Foo
5436
+ private_class_method :missing
5437
+ end
5438
+ ",
5439
+ );
5440
+ context.resolve();
5441
+
5442
+ assert_diagnostics_eq!(
5443
+ context,
5444
+ &[
5445
+ "undefined-method-visibility-target: undefined method `Foo::<Foo>#missing()` for visibility change (2:25-2:32)"
5446
+ ]
5447
+ );
5448
+
5449
+ context.index_uri(
5450
+ "file:///foo.rb",
5451
+ r"
5452
+ class Foo
5453
+ def self.missing; end
5454
+ private_class_method :missing
5455
+ end
5456
+ ",
5457
+ );
5458
+ context.resolve();
5459
+
5460
+ assert_no_diagnostics!(&context);
5461
+ assert_visibility_eq!(context, "Foo::<Foo>#missing()", Visibility::Private);
5462
+ }
5131
5463
  }
@@ -64,6 +64,25 @@ impl CDeclaration {
64
64
  }
65
65
  }
66
66
 
67
+ /// Convert a nullable C string to `Option<DeclarationId>`.
68
+ /// Null, empty, or non-UTF-8 input yields `None`.
69
+ ///
70
+ /// # Safety
71
+ ///
72
+ /// If non-null, `ptr` must point to a valid, NUL-terminated C string that remains valid for the
73
+ /// duration of the call. The contents do not need to be UTF-8 — non-UTF-8 input is handled by returning
74
+ /// `None`.
75
+ pub(crate) unsafe fn decl_id_from_char_ptr(ptr: *const c_char) -> Option<DeclarationId> {
76
+ if ptr.is_null() {
77
+ return None;
78
+ }
79
+ let s = unsafe { utils::convert_char_ptr_to_string(ptr) }.ok()?;
80
+ if s.is_empty() {
81
+ return None;
82
+ }
83
+ Some(DeclarationId::from(s.as_str()))
84
+ }
85
+
67
86
  /// An iterator over declaration IDs
68
87
  ///
69
88
  /// We snapshot the IDs at iterator creation so if the graph is modified, the iterator will not see the changes
@@ -178,42 +197,19 @@ pub unsafe extern "C" fn rdx_declaration_find_member(
178
197
 
179
198
  with_graph(pointer, |graph| {
180
199
  let id = DeclarationId::new(declaration_id);
200
+ let member_id = StringId::from(member_str.as_str());
181
201
 
182
- let Some(Declaration::Namespace(decl)) = graph.declarations().get(&id) else {
183
- return ptr::null();
202
+ let member_decl_id = match rubydex::query::find_member_in_ancestors(graph, id, member_id, only_inherited) {
203
+ Ok(decl_id) => decl_id,
204
+ Err(rubydex::query::FindMemberError::MemberNotFound) => return ptr::null(),
205
+ Err(err) => unreachable!(
206
+ "Namespace#find_member is only exposed on namespace declarations, so the declaration must exist and be \
207
+ a namespace, got {err:?}"
208
+ ),
184
209
  };
185
210
 
186
- let member_id = StringId::from(member_str.as_str());
187
- let mut found_main_namespace = false;
188
-
189
- decl.ancestors()
190
- .iter()
191
- .find_map(|ancestor| match ancestor {
192
- Ancestor::Complete(ancestor_id) => {
193
- // When only_inherited, skip self and prepended modules
194
- if only_inherited {
195
- let is_self = *ancestor_id == id;
196
- if is_self {
197
- found_main_namespace = true;
198
- }
199
- if is_self || !found_main_namespace {
200
- return None;
201
- }
202
- }
203
-
204
- let ancestor_decl = graph.declarations().get(ancestor_id).unwrap().as_namespace().unwrap();
205
-
206
- if let Some(member_decl_id) = ancestor_decl.member(&member_id) {
207
- return Some((member_decl_id, graph.declarations().get(member_decl_id).unwrap()));
208
- }
209
-
210
- None
211
- }
212
- Ancestor::Partial(_) => None,
213
- })
214
- .map_or(ptr::null(), |(member_decl_id, member_decl)| {
215
- Box::into_raw(Box::new(CDeclaration::from_declaration(*member_decl_id, member_decl))).cast_const()
216
- })
211
+ let member_decl = graph.declarations().get(&member_decl_id).unwrap();
212
+ Box::into_raw(Box::new(CDeclaration::from_declaration(member_decl_id, member_decl))).cast_const()
217
213
  })
218
214
  }
219
215
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  use crate::declaration_api::CDeclaration;
4
4
  use crate::declaration_api::DeclarationsIter;
5
+ use crate::declaration_api::decl_id_from_char_ptr;
5
6
  use crate::document_api::DocumentsIter;
6
7
  use crate::reference_api::{CConstantReference, CMethodReference, ConstantReferencesIter, MethodReferencesIter};
7
8
  use crate::{name_api, utils};
@@ -12,6 +13,7 @@ use rubydex::model::graph::Graph;
12
13
  use rubydex::model::ids::{DeclarationId, NameId, UriId};
13
14
  use rubydex::model::keywords;
14
15
  use rubydex::model::name::NameRef;
16
+ use rubydex::model::visibility::Visibility;
15
17
  use rubydex::query::{CompletionCandidate, CompletionContext, CompletionReceiver};
16
18
  use rubydex::resolution::Resolver;
17
19
  use rubydex::{indexing, integrity, listing, query};
@@ -791,11 +793,15 @@ fn run_and_finalize_completion(
791
793
  ///
792
794
  /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
793
795
  /// - `nesting` must point to `nesting_count` valid, null-terminated UTF-8 strings.
796
+ /// - `self_receiver` must be null or a valid, null-terminated UTF-8 string. When non-null, it
797
+ /// overrides the self-type (e.g., `"Foo::<Foo>"` for completion inside `def Foo.bar`), while
798
+ /// the lexical nesting still comes from `nesting`.
794
799
  #[unsafe(no_mangle)]
795
800
  pub unsafe extern "C" fn rdx_graph_complete_expression(
796
801
  pointer: GraphPointer,
797
802
  nesting: *const *const c_char,
798
803
  nesting_count: usize,
804
+ self_receiver: *const c_char,
799
805
  ) -> CompletionResult {
800
806
  with_mut_graph(pointer, |graph| {
801
807
  let Some((name_id, names_to_untrack)) = (unsafe { completion_nesting_name_id(graph, nesting, nesting_count) })
@@ -803,7 +809,16 @@ pub unsafe extern "C" fn rdx_graph_complete_expression(
803
809
  return CompletionResult::success(ptr::null_mut());
804
810
  };
805
811
 
806
- run_and_finalize_completion(graph, CompletionReceiver::Expression(name_id), names_to_untrack)
812
+ let self_decl_id = unsafe { decl_id_from_char_ptr(self_receiver) };
813
+
814
+ run_and_finalize_completion(
815
+ graph,
816
+ CompletionReceiver::Expression {
817
+ self_decl_id,
818
+ nesting_name_id: name_id,
819
+ },
820
+ names_to_untrack,
821
+ )
807
822
  })
808
823
  }
809
824
 
@@ -815,19 +830,27 @@ pub unsafe extern "C" fn rdx_graph_complete_expression(
815
830
  ///
816
831
  /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
817
832
  /// - `name` must be a valid, null-terminated UTF-8 string (FQN of the namespace).
833
+ /// - `self_receiver` must be null or a valid, null-terminated UTF-8 string. When non-null, it
834
+ /// is the caller's runtime self type (e.g., for filtering `private_class_method` visibility).
818
835
  #[unsafe(no_mangle)]
819
836
  pub unsafe extern "C" fn rdx_graph_complete_namespace_access(
820
837
  pointer: GraphPointer,
821
838
  name: *const c_char,
839
+ self_receiver: *const c_char,
822
840
  ) -> CompletionResult {
823
841
  let Ok(name_str) = (unsafe { utils::convert_char_ptr_to_string(name) }) else {
824
842
  return CompletionResult::success(ptr::null_mut());
825
843
  };
826
844
 
827
845
  with_mut_graph(pointer, |graph| {
846
+ let self_decl_id = unsafe { decl_id_from_char_ptr(self_receiver) };
847
+
828
848
  run_and_finalize_completion(
829
849
  graph,
830
- CompletionReceiver::NamespaceAccess(DeclarationId::from(name_str.as_str())),
850
+ CompletionReceiver::NamespaceAccess {
851
+ self_decl_id,
852
+ namespace_decl_id: DeclarationId::from(name_str.as_str()),
853
+ },
831
854
  Vec::new(),
832
855
  )
833
856
  })
@@ -841,19 +864,27 @@ pub unsafe extern "C" fn rdx_graph_complete_namespace_access(
841
864
  ///
842
865
  /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
843
866
  /// - `name` must be a valid, null-terminated UTF-8 string (FQN of the receiver).
867
+ /// - `self_receiver` must be null or a valid, null-terminated UTF-8 string. When non-null, it
868
+ /// is the caller's runtime self type, used for MRI-style visibility checks.
844
869
  #[unsafe(no_mangle)]
845
870
  pub unsafe extern "C" fn rdx_graph_complete_method_call(
846
871
  pointer: GraphPointer,
847
872
  name: *const c_char,
873
+ self_receiver: *const c_char,
848
874
  ) -> CompletionResult {
849
875
  let Ok(name_str) = (unsafe { utils::convert_char_ptr_to_string(name) }) else {
850
876
  return CompletionResult::success(ptr::null_mut());
851
877
  };
852
878
 
853
879
  with_mut_graph(pointer, |graph| {
880
+ let self_decl_id = unsafe { decl_id_from_char_ptr(self_receiver) };
881
+
854
882
  run_and_finalize_completion(
855
883
  graph,
856
- CompletionReceiver::MethodCall(DeclarationId::from(name_str.as_str())),
884
+ CompletionReceiver::MethodCall {
885
+ self_decl_id,
886
+ receiver_decl_id: DeclarationId::from(name_str.as_str()),
887
+ },
857
888
  Vec::new(),
858
889
  )
859
890
  })
@@ -868,28 +899,34 @@ pub unsafe extern "C" fn rdx_graph_complete_method_call(
868
899
  /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
869
900
  /// - `name` must be a valid, null-terminated UTF-8 string (FQN of the method).
870
901
  /// - `nesting` must point to `nesting_count` valid, null-terminated UTF-8 strings.
902
+ /// - `self_receiver` must be null or a valid, null-terminated UTF-8 string. See
903
+ /// `rdx_graph_complete_expression` for semantics.
871
904
  #[unsafe(no_mangle)]
872
905
  pub unsafe extern "C" fn rdx_graph_complete_method_argument(
873
906
  pointer: GraphPointer,
874
907
  name: *const c_char,
875
908
  nesting: *const *const c_char,
876
909
  nesting_count: usize,
910
+ self_receiver: *const c_char,
877
911
  ) -> CompletionResult {
878
912
  let Ok(name_str) = (unsafe { utils::convert_char_ptr_to_string(name) }) else {
879
913
  return CompletionResult::success(ptr::null_mut());
880
914
  };
881
915
 
882
916
  with_mut_graph(pointer, |graph| {
883
- let Some((self_name_id, names_to_untrack)) =
917
+ let Some((nesting_name_id, names_to_untrack)) =
884
918
  (unsafe { completion_nesting_name_id(graph, nesting, nesting_count) })
885
919
  else {
886
920
  return CompletionResult::success(ptr::null_mut());
887
921
  };
888
922
 
923
+ let self_decl_id = unsafe { decl_id_from_char_ptr(self_receiver) };
924
+
889
925
  run_and_finalize_completion(
890
926
  graph,
891
927
  CompletionReceiver::MethodArgument {
892
- self_name_id,
928
+ self_decl_id,
929
+ nesting_name_id,
893
930
  method_decl_id: DeclarationId::from(name_str.as_str()),
894
931
  },
895
932
  names_to_untrack,
@@ -942,6 +979,53 @@ pub unsafe extern "C" fn rdx_keyword_get(name: *const c_char) -> *const CKeyword
942
979
  }
943
980
  }
944
981
 
982
+ #[repr(u8)]
983
+ #[derive(Debug, Clone, Copy)]
984
+ pub enum CVisibility {
985
+ Public = 0,
986
+ Protected = 1,
987
+ Private = 2,
988
+ }
989
+
990
+ /// Returns the visibility of a declaration (method, constant, class, or module) as a heap-allocated
991
+ /// `CVisibility`, or NULL when the declaration carries no visibility (e.g. variables, singleton
992
+ /// classes, todos). Caller must free the returned pointer with `free_c_visibility`.
993
+ ///
994
+ /// # Safety
995
+ ///
996
+ /// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
997
+ #[unsafe(no_mangle)]
998
+ pub unsafe extern "C" fn rdx_graph_visibility(pointer: GraphPointer, declaration_id: u64) -> *const CVisibility {
999
+ with_graph(pointer, |graph| {
1000
+ let Some(visibility) = graph.visibility(&DeclarationId::new(declaration_id)) else {
1001
+ return ptr::null();
1002
+ };
1003
+
1004
+ let c_visibility = match visibility {
1005
+ Visibility::Public => CVisibility::Public,
1006
+ Visibility::Protected => CVisibility::Protected,
1007
+ Visibility::Private => CVisibility::Private,
1008
+ Visibility::ModuleFunction => {
1009
+ unimplemented!("module_function visibility translation is not implemented yet")
1010
+ }
1011
+ };
1012
+
1013
+ Box::into_raw(Box::new(c_visibility)).cast_const()
1014
+ })
1015
+ }
1016
+
1017
+ /// Frees a `CVisibility` previously returned by `rdx_graph_visibility`.
1018
+ ///
1019
+ /// # Safety
1020
+ ///
1021
+ /// - `ptr` must be a valid pointer previously returned by `rdx_graph_visibility`.
1022
+ #[unsafe(no_mangle)]
1023
+ pub unsafe extern "C" fn free_c_visibility(ptr: *const CVisibility) {
1024
+ unsafe {
1025
+ let _ = Box::from_raw(ptr.cast_mut());
1026
+ }
1027
+ }
1028
+
945
1029
  /// Frees a `CKeyword` previously returned by `rdx_keyword_get`.
946
1030
  ///
947
1031
  /// # Safety
@@ -76,4 +76,5 @@ pub mod graph_api;
76
76
  pub mod location_api;
77
77
  pub mod name_api;
78
78
  pub mod reference_api;
79
+ pub mod signature_api;
79
80
  pub mod utils;