rubydex 0.1.0.beta13 → 0.1.0.beta14

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 922611abae468890ba5fcf77e365aba7ac79754c5db93abeec4ad67fb84469a0
4
- data.tar.gz: 75ccad3efb46e07f582e732452be8d388183ee2030e2569c1a19a07844ab20f8
3
+ metadata.gz: 55c47ced758d8aff486a445a0c15a1ffe6525b5d2ee59fea8d38be40bc791207
4
+ data.tar.gz: 9e73f9244f197ed1b062f475ddc077b25f1dd0a5d8c29de430c7b256b20c380e
5
5
  SHA512:
6
- metadata.gz: 4810b81cca5c1aceb7546f1e75f348217ae8b0f2b9559e315b58e96eb8a62ca5603c68ae863da43272830feef452389b390c1b2786cfc3ff0944dc0e1096acb2
7
- data.tar.gz: e119905f38a21842a543053e0218cd29559c17865e81485fd7f54d74f43782f445902b166d1cd20c28dcd4b5bdf39533122f8cff38a81201f6c25868bb78798f
6
+ metadata.gz: 50444ddd1ca5576961316b2c8db705a872eaf658366587d11f9cc2860b2f97f517257ffbcc51bf9fbfa0036e436b5fe338479431c90e285ad155048616f77897
7
+ data.tar.gz: 856b302c492ccc0ba74a4ea1dc5c2fc993ba43a47a0f635ee005af764ae39c1be61a50fcc79f8b151c1b20e77a7e0b719115fe3427fed880ed0f0cbdd47fed45
@@ -407,6 +407,28 @@ static VALUE rdxr_variable_declaration_references(VALUE self) {
407
407
  return rb_ary_new();
408
408
  }
409
409
 
410
+ // ConstantAlias#target -> Declaration?
411
+ // Returns the first resolved target declaration for this constant alias, or nil if none of its definitions resolved to
412
+ // a target
413
+ static VALUE rdxr_constant_alias_target(VALUE self) {
414
+ HandleData *data;
415
+ TypedData_Get_Struct(self, HandleData, &handle_type, data);
416
+
417
+ void *graph;
418
+ TypedData_Get_Struct(data->graph_obj, void *, &graph_type, graph);
419
+
420
+ const CDeclaration *decl = rdx_constant_alias_target(graph, data->id);
421
+ if (decl == NULL) {
422
+ return Qnil;
423
+ }
424
+
425
+ VALUE decl_class = rdxi_declaration_class_for_kind(decl->kind);
426
+ VALUE argv[] = {data->graph_obj, ULL2NUM(decl->id)};
427
+ free_c_declaration(decl);
428
+
429
+ return rb_class_new_instance(2, argv, decl_class);
430
+ }
431
+
410
432
  void rdxi_initialize_declaration(VALUE mRubydex) {
411
433
  cDeclaration = rb_define_class_under(mRubydex, "Declaration", rb_cObject);
412
434
  cNamespace = rb_define_class_under(mRubydex, "Namespace", cDeclaration);
@@ -440,6 +462,7 @@ void rdxi_initialize_declaration(VALUE mRubydex) {
440
462
  // Constant and ConstantAlias have constant references
441
463
  rb_define_method(cConstant, "references", rdxr_constant_declaration_references, 0);
442
464
  rb_define_method(cConstantAlias, "references", rdxr_constant_declaration_references, 0);
465
+ rb_define_method(cConstantAlias, "target", rdxr_constant_alias_target, 0);
443
466
 
444
467
  // Method has method references
445
468
  rb_define_method(cMethod, "references", rdxr_method_declaration_references, 0);
@@ -86,11 +86,15 @@ copy_dylib_commands = if Gem.win_platform?
86
86
  ""
87
87
  elsif RUBY_PLATFORM.include?("darwin")
88
88
  src_dylib = target_dir.join("librubydex_sys.dylib")
89
- "\t$(COPY) #{src_dylib} #{lib_dir}"
89
+ dst_dylib = lib_dir.join("librubydex_sys.dylib")
90
+ # Unlink before copy so the new dylib gets a fresh inode. Overwriting in place while another process
91
+ # (e.g. the Ruby LSP) has the old dylib mmap'd triggers a macOS code-signing SIGKILL on its next page fault.
92
+ "\t$(Q)$(RM) #{dst_dylib}\n\t$(COPY) #{src_dylib} #{lib_dir}"
90
93
  else
91
94
  # Linux
92
95
  src_dylib = target_dir.join("librubydex_sys.so")
93
- "\t$(COPY) #{src_dylib} #{lib_dir}"
96
+ dst_dylib = lib_dir.join("librubydex_sys.so")
97
+ "\t$(Q)$(RM) #{dst_dylib}\n\t$(COPY) #{src_dylib} #{lib_dir}"
94
98
  end
95
99
 
96
100
  rust_srcs = Dir.glob("#{root_dir}/**/*.rs").reject { |path| path.include?("rust/target") }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubydex
4
- VERSION = "0.1.0.beta13"
4
+ VERSION = "0.1.0.beta14"
5
5
  end
data/rbi/rubydex.rbi CHANGED
@@ -76,6 +76,9 @@ end
76
76
  class Rubydex::ConstantAlias < Rubydex::Declaration
77
77
  sig { returns(T::Enumerable[Rubydex::ConstantReference]) }
78
78
  def references; end
79
+
80
+ sig { returns(T.nilable(Rubydex::Declaration)) }
81
+ def target; end
79
82
  end
80
83
 
81
84
  class Rubydex::ClassVariable < Rubydex::Declaration
@@ -107,4 +107,5 @@ rules! {
107
107
  InvalidMethodVisibility;
108
108
 
109
109
  // Resolution
110
+ UndefinedMethodVisibilityTarget;
110
111
  }
@@ -1507,9 +1507,10 @@ impl Visit<'_> for RubyIndexer<'_> {
1507
1507
  };
1508
1508
 
1509
1509
  let string_id = self.local_graph.intern_string(singleton_class_name);
1510
+ let nesting = self.current_lexical_scope_name_id();
1510
1511
  let name_id = self
1511
1512
  .local_graph
1512
- .add_name(Name::new(string_id, ParentScope::Attached(attached_target), None));
1513
+ .add_name(Name::new(string_id, ParentScope::Attached(attached_target), nesting));
1513
1514
 
1514
1515
  let definition = Definition::SingletonClass(Box::new(SingletonClassDefinition::new(
1515
1516
  name_id,
@@ -14,6 +14,7 @@ use crate::model::ids::{ConstantReferenceId, DeclarationId, DefinitionId, Method
14
14
  use crate::model::name::{Name, NameRef, ParentScope, ResolvedName};
15
15
  use crate::model::references::{ConstantReference, MethodRef};
16
16
  use crate::model::string_ref::StringRef;
17
+ use crate::model::visibility::Visibility;
17
18
  use crate::stats;
18
19
 
19
20
  /// An entity whose validity depends on a particular `NameId`.
@@ -601,6 +602,44 @@ impl Graph {
601
602
  &self.name_dependents
602
603
  }
603
604
 
605
+ /// Returns the visibility for a method declaration.
606
+ ///
607
+ /// Scans the declaration's backing definitions from the end:
608
+ /// - First `MethodVisibilityDefinition` wins
609
+ /// - Otherwise, falls back to the latest method-like definition's indexed visibility
610
+ /// - Returns `None` if the declaration has no definitions with visibility
611
+ #[must_use]
612
+ pub fn visibility(&self, declaration_id: &DeclarationId) -> Option<Visibility> {
613
+ let declaration = self.declarations.get(declaration_id)?;
614
+ let definitions = declaration.definitions();
615
+
616
+ // Scan from the end: last visibility directive wins
617
+ for def_id in definitions.iter().rev() {
618
+ if let Some(definition) = self.definitions.get(def_id) {
619
+ match definition {
620
+ Definition::MethodVisibility(vis) => return Some(*vis.visibility()),
621
+ Definition::Method(method) => return Some(*method.visibility()),
622
+ Definition::AttrAccessor(attr) => return Some(*attr.visibility()),
623
+ Definition::AttrReader(attr) => return Some(*attr.visibility()),
624
+ Definition::AttrWriter(attr) => return Some(*attr.visibility()),
625
+ Definition::Class(_)
626
+ | Definition::SingletonClass(_)
627
+ | Definition::Module(_)
628
+ | Definition::Constant(_)
629
+ | Definition::ConstantAlias(_)
630
+ | Definition::ConstantVisibility(_)
631
+ | Definition::GlobalVariable(_)
632
+ | Definition::InstanceVariable(_)
633
+ | Definition::ClassVariable(_)
634
+ | Definition::MethodAlias(_)
635
+ | Definition::GlobalVariableAlias(_) => {}
636
+ }
637
+ }
638
+ }
639
+
640
+ None
641
+ }
642
+
604
643
  /// Drains the accumulated work items, returning them for use by the resolver.
605
644
  pub fn take_pending_work(&mut self) -> Vec<Unit> {
606
645
  std::mem::take(&mut self.pending_work)
@@ -2234,14 +2273,14 @@ mod tests {
2234
2273
  assert_eq!(13, context.graph().constant_references.len());
2235
2274
  assert_eq!(2, context.graph().method_references.len());
2236
2275
  assert_eq!(2, context.graph().documents.len());
2237
- assert_eq!(19, context.graph().names.len());
2276
+ assert_eq!(20, context.graph().names.len());
2238
2277
  assert_eq!(47, context.graph().strings.len());
2239
2278
  context.index_uri("file:///foo.rb", source);
2240
2279
  assert_eq!(49, context.graph().definitions.len());
2241
2280
  assert_eq!(13, context.graph().constant_references.len());
2242
2281
  assert_eq!(2, context.graph().method_references.len());
2243
2282
  assert_eq!(2, context.graph().documents.len());
2244
- assert_eq!(19, context.graph().names.len());
2283
+ assert_eq!(20, context.graph().names.len());
2245
2284
  assert_eq!(47, context.graph().strings.len());
2246
2285
  }
2247
2286
 
@@ -897,7 +897,7 @@ mod tests {
897
897
  context.resolve();
898
898
 
899
899
  let foo_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
900
- let name_id = Name::new(StringId::from("<Foo>"), ParentScope::Attached(foo_id), None).id();
900
+ let name_id = Name::new(StringId::from("<Foo>"), ParentScope::Attached(foo_id), Some(foo_id)).id();
901
901
  assert_declaration_completion_eq!(
902
902
  context,
903
903
  CompletionReceiver::Expression(name_id),
@@ -3,6 +3,7 @@ use std::{
3
3
  hash::BuildHasher,
4
4
  };
5
5
 
6
+ use crate::diagnostic::{Diagnostic, Rule};
6
7
  use crate::model::{
7
8
  built_in::{BASIC_OBJECT_ID, CLASS_ID, KERNEL_ID, MODULE_ID, OBJECT_ID},
8
9
  declaration::{
@@ -272,6 +273,8 @@ impl<'a> Resolver<'a> {
272
273
  /// Handle other definitions that don't require resolution, but need to have their declarations and membership created
273
274
  #[allow(clippy::too_many_lines)]
274
275
  fn handle_remaining_definitions(&mut self, other_ids: Vec<DefinitionId>) {
276
+ let mut method_visibility_ids = Vec::new();
277
+
275
278
  for id in other_ids {
276
279
  match self.graph.definitions().get(&id).unwrap() {
277
280
  Definition::Method(method_definition) => {
@@ -538,8 +541,8 @@ impl<'a> Resolver<'a> {
538
541
  Definition::ConstantVisibility(_constant_visibility) => {
539
542
  // TODO
540
543
  }
541
- Definition::MethodVisibility(_method_visibility) => {
542
- // TODO
544
+ Definition::MethodVisibility(_) => {
545
+ method_visibility_ids.push(id);
543
546
  }
544
547
  Definition::Class(_)
545
548
  | Definition::SingletonClass(_)
@@ -550,6 +553,91 @@ impl<'a> Resolver<'a> {
550
553
  }
551
554
  }
552
555
  }
556
+
557
+ self.resolve_method_visibilities(method_visibility_ids);
558
+ }
559
+
560
+ /// Resolves retroactive method visibility changes (`private :foo`, `protected :foo`, `public :foo`).
561
+ ///
562
+ /// Runs as a second pass after all methods/attrs are declared, so `private :bar` works
563
+ /// regardless of whether `def bar` appeared before or after it in source.
564
+ fn resolve_method_visibilities(&mut self, visibility_ids: Vec<DefinitionId>) {
565
+ let mut pending_work = Vec::new();
566
+
567
+ for id in visibility_ids {
568
+ let Definition::MethodVisibility(method_visibility) = self.graph.definitions().get(&id).unwrap() else {
569
+ unreachable!()
570
+ };
571
+
572
+ let str_id = *method_visibility.str_id();
573
+ let uri_id = *method_visibility.uri_id();
574
+ let offset = method_visibility.offset().clone();
575
+ let lexical_nesting_id = *method_visibility.lexical_nesting_id();
576
+
577
+ let Some(owner_id) = self.resolve_lexical_owner(lexical_nesting_id) else {
578
+ continue;
579
+ };
580
+
581
+ let Some(Declaration::Namespace(namespace)) = self.graph.declarations().get(&owner_id) else {
582
+ continue;
583
+ };
584
+
585
+ let mut visibility_applied = false;
586
+ let mut has_partial = false;
587
+
588
+ for ancestor in namespace.ancestors() {
589
+ match ancestor {
590
+ Ancestor::Complete(ancestor_id) => {
591
+ let has_member = self
592
+ .graph
593
+ .declarations()
594
+ .get(ancestor_id)
595
+ .and_then(|decl| decl.as_namespace())
596
+ .and_then(|ns| ns.member(&str_id))
597
+ .is_some();
598
+
599
+ if has_member {
600
+ // Direct member: `create_declaration`'s fully qualified name dedup attaches
601
+ // this visibility definition to the existing method declaration.
602
+ // Inherited: a new child-owned declaration is created.
603
+ self.create_declaration(str_id, id, owner_id, |name| {
604
+ Declaration::Method(Box::new(MethodDeclaration::new(name, owner_id)))
605
+ });
606
+ visibility_applied = true;
607
+ break;
608
+ }
609
+ }
610
+ Ancestor::Partial(_) => has_partial = true,
611
+ }
612
+ }
613
+
614
+ if visibility_applied {
615
+ continue;
616
+ }
617
+
618
+ if has_partial {
619
+ // Method might exist on an unresolved ancestor — requeue for retry.
620
+ pending_work.push(Unit::Definition(id));
621
+ } else {
622
+ // Ancestors are fully resolved — method definitively doesn't exist.
623
+ let method_name = self.graph.strings().get(&str_id).unwrap().as_str().to_string();
624
+ let owner_name = self.graph.declarations().get(&owner_id).unwrap().name().to_string();
625
+ let diagnostic = Diagnostic::new(
626
+ Rule::UndefinedMethodVisibilityTarget,
627
+ uri_id,
628
+ offset,
629
+ format!("undefined method `{method_name}` for visibility change in `{owner_name}`"),
630
+ );
631
+ self.graph
632
+ .declarations_mut()
633
+ .get_mut(&owner_id)
634
+ .unwrap()
635
+ .add_diagnostic(diagnostic);
636
+ }
637
+ }
638
+
639
+ // Must extend work here so incremental resolution can resolve previously unresolved visibility operations
640
+ self.graph.extend_work(pending_work);
553
641
  }
554
642
 
555
643
  fn create_declaration<F>(