rubydex 0.3.0 → 0.4.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -7
  3. data/THIRD_PARTY_LICENSES.html +238 -2
  4. data/exe/rdx +2 -149
  5. data/ext/rubydex/config.c +140 -0
  6. data/ext/rubydex/config.h +16 -0
  7. data/ext/rubydex/diagnostic.c +75 -1
  8. data/ext/rubydex/diagnostic.h +2 -0
  9. data/ext/rubydex/graph.c +13 -45
  10. data/ext/rubydex/graph.h +6 -0
  11. data/ext/rubydex/query.c +398 -16
  12. data/ext/rubydex/rubydex.c +2 -0
  13. data/ext/rubydex/utils.c +11 -4
  14. data/lib/ruby_lsp/rubydex/addon.rb +211 -0
  15. data/lib/rubydex/cli/command/console.rb +55 -0
  16. data/lib/rubydex/cli/command/lint/explain.rb +74 -0
  17. data/lib/rubydex/cli/command/lint.rb +202 -0
  18. data/lib/rubydex/cli/command/mcp.rb +30 -0
  19. data/lib/rubydex/cli/command/query.rb +70 -0
  20. data/lib/rubydex/cli/command/skill.rb +69 -0
  21. data/lib/rubydex/cli/command.rb +168 -0
  22. data/lib/rubydex/cli.rb +93 -0
  23. data/lib/rubydex/config.rb +59 -0
  24. data/lib/rubydex/diagnostic.rb +12 -3
  25. data/lib/rubydex/errors.rb +42 -1
  26. data/lib/rubydex/graph.rb +10 -3
  27. data/lib/rubydex/linter/custom_rule.rb +97 -0
  28. data/lib/rubydex/linter/helpers/path_helpers.rb +78 -0
  29. data/lib/rubydex/linter/helpers/source_access_helpers.rb +31 -0
  30. data/lib/rubydex/linter/rule_loader.rb +36 -0
  31. data/lib/rubydex/linter/rule_test_case.rb +343 -0
  32. data/lib/rubydex/linter/runner.rb +56 -0
  33. data/lib/rubydex/linter.rb +19 -0
  34. data/lib/rubydex/location.rb +3 -0
  35. data/lib/rubydex/mcp_server.rb +1 -2
  36. data/lib/rubydex/related_information.rb +17 -0
  37. data/lib/rubydex/rule.rb +33 -0
  38. data/lib/rubydex/severity.rb +70 -0
  39. data/lib/rubydex/skill.rb +88 -0
  40. data/lib/rubydex/skill_registry.rb +62 -0
  41. data/lib/rubydex/version.rb +1 -1
  42. data/lib/rubydex.rb +6 -0
  43. data/lib/rubydex_linter/rules/rule_structure.rb +125 -0
  44. data/rbi/rubydex.rbi +558 -17
  45. data/rust/Cargo.lock +2 -2
  46. data/rust/rubydex/Cargo.toml +1 -1
  47. data/rust/rubydex/benches/graph_memory.rs +3 -5
  48. data/rust/rubydex/src/config.rs +538 -157
  49. data/rust/rubydex/src/diagnostic.rs +66 -40
  50. data/rust/rubydex/src/errors.rs +0 -1
  51. data/rust/rubydex/src/indexing/local_graph.rs +6 -5
  52. data/rust/rubydex/src/indexing/rbs_indexer.rs +270 -6
  53. data/rust/rubydex/src/indexing/ruby_indexer.rs +10 -12
  54. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +134 -81
  55. data/rust/rubydex/src/lib.rs +1 -0
  56. data/rust/rubydex/src/listing.rs +26 -1
  57. data/rust/rubydex/src/main.rs +7 -4
  58. data/rust/rubydex/src/model/declaration.rs +302 -219
  59. data/rust/rubydex/src/model/graph.rs +27 -40
  60. data/rust/rubydex/src/model/name.rs +58 -17
  61. data/rust/rubydex/src/operation/ruby_builder.rs +30 -45
  62. data/rust/rubydex/src/path_helpers.rs +77 -0
  63. data/rust/rubydex/src/query/cypher/schema.rs +63 -0
  64. data/rust/rubydex/src/query/cypher/tests.rs +26 -1
  65. data/rust/rubydex/src/query/cypher.rs +8 -11
  66. data/rust/rubydex/src/query.rs +123 -43
  67. data/rust/rubydex/src/resolution.rs +139 -187
  68. data/rust/rubydex/src/resolution_tests.rs +247 -19
  69. data/rust/rubydex/src/test_utils/context.rs +2 -1
  70. data/rust/rubydex/src/test_utils/graph_test.rs +26 -12
  71. data/rust/rubydex/src/test_utils/local_graph_test.rs +19 -0
  72. data/rust/rubydex/tests/cli.rs +4 -4
  73. data/rust/rubydex-sys/src/config_api.rs +205 -0
  74. data/rust/rubydex-sys/src/cypher_api.rs +791 -0
  75. data/rust/rubydex-sys/src/diagnostic_api.rs +77 -8
  76. data/rust/rubydex-sys/src/graph_api.rs +13 -194
  77. data/rust/rubydex-sys/src/lib.rs +2 -0
  78. data/rust/rubydex-sys/src/name_api.rs +2 -6
  79. data/rust/rubydex-sys/src/utils.rs +37 -0
  80. data/skills/send-private-method/SKILL.md +133 -0
  81. metadata +31 -2
@@ -1,6 +1,6 @@
1
1
  #[cfg(any(test, feature = "test_utils"))]
2
2
  use crate::model::document::Document;
3
- use crate::{model::ids::UriId, offset::Offset};
3
+ use crate::{assert_mem_size, model::ids::UriId, offset::Offset};
4
4
 
5
5
  #[derive(Debug)]
6
6
  pub struct Diagnostic {
@@ -9,6 +9,7 @@ pub struct Diagnostic {
9
9
  offset: Offset,
10
10
  message: String,
11
11
  }
12
+ assert_mem_size!(Diagnostic, 48);
12
13
 
13
14
  impl Diagnostic {
14
15
  #[must_use]
@@ -53,60 +54,85 @@ impl Diagnostic {
53
54
  }
54
55
  }
55
56
 
56
- fn camel_to_snake(s: &str) -> String {
57
- let mut snake = String::new();
58
- for (i, ch) in s.chars().enumerate() {
59
- if ch.is_uppercase() {
60
- if i != 0 {
61
- snake.push('-');
62
- }
63
- for lc in ch.to_lowercase() {
64
- snake.push(lc);
65
- }
66
- } else {
67
- snake.push(ch);
68
- }
69
- }
70
- snake
57
+ #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Deserialize)]
58
+ #[serde(rename_all = "lowercase")]
59
+ pub enum Severity {
60
+ Error,
61
+ Warning,
62
+ Information,
63
+ Hint,
71
64
  }
72
65
 
73
66
  macro_rules! rules {
74
- (
75
- $( $variant:ident );* $(;)?
76
- ) => {
67
+ ($($(#[$attribute:meta])* $rule:ident),+ $(,)?) => {
77
68
  #[derive(Debug, Copy, Clone, PartialEq, Eq)]
78
69
  pub enum Rule {
79
- $(
80
- $variant,
81
- )*
70
+ $($(#[$attribute])* $rule,)+
82
71
  }
83
72
 
84
- impl std::fmt::Display for Rule {
85
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86
- write!(f, "{}", match self {
87
- $(
88
- Rule::$variant => camel_to_snake(stringify!($variant)),
89
- )*
90
- })
73
+ impl Rule {
74
+ #[must_use]
75
+ pub fn all() -> &'static [Self] {
76
+ &[$(Self::$rule,)+]
91
77
  }
92
78
  }
93
- }
79
+ };
94
80
  }
95
81
 
96
82
  rules! {
97
83
  // Parsing
98
- ParseError;
99
- ParseWarning;
84
+ ParseError,
85
+ ParseWarning,
100
86
 
101
87
  // Indexing
102
- DynamicConstantReference;
103
- DynamicSingletonDefinition;
104
- DynamicAncestor;
105
- TopLevelMixinSelf;
106
- InvalidConstantVisibility;
107
- InvalidMethodVisibility;
88
+ DynamicConstantReference,
89
+ DynamicSingletonDefinition,
90
+ DynamicAncestor,
91
+ TopLevelMixinSelf,
92
+ InvalidConstantVisibility,
93
+ InvalidMethodVisibility,
108
94
 
109
95
  // Resolution
110
- UndefinedMethodVisibilityTarget;
111
- UndefinedConstantVisibilityTarget;
96
+ UndefinedMethodVisibilityTarget,
97
+ UndefinedConstantVisibilityTarget,
98
+ }
99
+
100
+ impl Rule {
101
+ #[must_use]
102
+ pub fn name(&self) -> &'static str {
103
+ match self {
104
+ Self::ParseError => "ParseError",
105
+ Self::ParseWarning => "ParseWarning",
106
+ Self::DynamicConstantReference => "DynamicConstantReference",
107
+ Self::DynamicSingletonDefinition => "DynamicSingletonDefinition",
108
+ Self::DynamicAncestor => "DynamicAncestor",
109
+ Self::TopLevelMixinSelf => "TopLevelMixinSelf",
110
+ Self::InvalidConstantVisibility => "InvalidConstantVisibility",
111
+ Self::InvalidMethodVisibility => "InvalidMethodVisibility",
112
+ Self::UndefinedMethodVisibilityTarget => "UndefinedMethodVisibilityTarget",
113
+ Self::UndefinedConstantVisibilityTarget => "UndefinedConstantVisibilityTarget",
114
+ }
115
+ }
116
+
117
+ #[must_use]
118
+ pub fn default_severity(&self) -> Severity {
119
+ match self {
120
+ Self::ParseError => Severity::Error,
121
+ Self::ParseWarning
122
+ | Self::InvalidConstantVisibility
123
+ | Self::InvalidMethodVisibility
124
+ | Self::UndefinedMethodVisibilityTarget
125
+ | Self::UndefinedConstantVisibilityTarget => Severity::Warning,
126
+ Self::DynamicConstantReference
127
+ | Self::DynamicSingletonDefinition
128
+ | Self::DynamicAncestor
129
+ | Self::TopLevelMixinSelf => Severity::Information,
130
+ }
131
+ }
132
+ }
133
+
134
+ impl std::fmt::Display for Rule {
135
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136
+ write!(f, "{}", self.name())
137
+ }
112
138
  }
@@ -26,5 +26,4 @@ macro_rules! errors {
26
26
  errors!(
27
27
  FileError;
28
28
  ConfigError;
29
- ConfigNotFound;
30
29
  );
@@ -6,7 +6,7 @@ use crate::model::document::Document;
6
6
  use crate::model::graph::NameDependent;
7
7
  use crate::model::identity_maps::IdentityHashMap;
8
8
  use crate::model::ids::{ConstantReferenceId, DefinitionId, MethodReferenceId, NameId, StringId, UriId};
9
- use crate::model::name::{Name, NameRef};
9
+ use crate::model::name::{Name, NameRef, ParentScope};
10
10
  use crate::model::references::{ConstantReference, MethodRef};
11
11
  use crate::model::string_ref::StringRef;
12
12
  use crate::offset::Offset;
@@ -119,7 +119,8 @@ impl LocalGraph {
119
119
  &self.names
120
120
  }
121
121
 
122
- pub fn add_name(&mut self, name: Name) -> NameId {
122
+ pub fn add_name(&mut self, str: StringId, parent_scope: ParentScope, nesting: Option<NameId>) -> NameId {
123
+ let name = Name::new(&self.names, str, parent_scope, nesting);
123
124
  let name_id = name.id();
124
125
 
125
126
  match self.names.entry(name_id) {
@@ -128,13 +129,13 @@ impl LocalGraph {
128
129
  entry.get_mut().increment_ref_count(1);
129
130
  }
130
131
  Entry::Vacant(entry) => {
131
- if let Some(&parent_scope) = name.parent_scope().as_ref() {
132
+ if let Some(&parent_scope_id) = parent_scope.as_ref() {
132
133
  self.name_dependents
133
- .entry(parent_scope)
134
+ .entry(parent_scope_id)
134
135
  .or_default()
135
136
  .push(NameDependent::ChildName(name_id));
136
137
  }
137
- if let Some(&nesting_id) = name.nesting().as_ref() {
138
+ if let Some(nesting_id) = nesting {
138
139
  self.name_dependents
139
140
  .entry(nesting_id)
140
141
  .or_default()
@@ -3,8 +3,9 @@
3
3
  use core::panic;
4
4
 
5
5
  use ruby_rbs::node::{
6
- self, AliasKind, ClassNode, CommentNode, ConstantNode, ExtendNode, FunctionTypeNode, GlobalNode, IncludeNode,
7
- ModuleNode, Node, NodeList, PrependNode, TypeNameNode, Visit,
6
+ self, AliasKind, AttrAccessorNode, AttrReaderNode, AttrWriterNode, AttributeKind, AttributeVisibility, ClassNode,
7
+ CommentNode, ConstantNode, ExtendNode, FunctionTypeNode, GlobalNode, IncludeNode, ModuleNode, Node, NodeList,
8
+ PrependNode, TypeNameNode, Visit,
8
9
  };
9
10
 
10
11
  use crate::diagnostic::Rule;
@@ -17,7 +18,7 @@ use crate::model::definitions::{
17
18
  };
18
19
  use crate::model::document::Document;
19
20
  use crate::model::ids::{ConstantReferenceId, DefinitionId, NameId, StringId, UriId};
20
- use crate::model::name::{Name, ParentScope};
21
+ use crate::model::name::ParentScope;
21
22
  use crate::model::references::ConstantReference;
22
23
  use crate::model::visibility::Visibility;
23
24
  use crate::offset::Offset;
@@ -102,8 +103,7 @@ impl<'a> RBSIndexer<'a> {
102
103
  nesting_name_id: Option<NameId>,
103
104
  ) -> NameId {
104
105
  let string_id = self.local_graph.intern_string(symbol.as_str().to_owned());
105
- self.local_graph
106
- .add_name(Name::new(string_id, parent_scope, nesting_name_id))
106
+ self.local_graph.add_name(string_id, parent_scope, nesting_name_id)
107
107
  }
108
108
 
109
109
  fn parent_lexical_scope_id(&self) -> Option<DefinitionId> {
@@ -223,6 +223,133 @@ impl<'a> RBSIndexer<'a> {
223
223
  definition_id
224
224
  }
225
225
 
226
+ #[allow(clippy::too_many_arguments)]
227
+ fn register_attribute_methods(
228
+ &mut self,
229
+ name: &str,
230
+ offset: Offset,
231
+ name_offset: Offset,
232
+ comments: Box<[Comment]>,
233
+ flags: DefinitionFlags,
234
+ lexical_nesting_id: Option<DefinitionId>,
235
+ kind: AttributeKind,
236
+ attribute_visibility: AttributeVisibility,
237
+ reader: bool,
238
+ writer: bool,
239
+ ) {
240
+ let (visibility, receiver) = match kind {
241
+ AttributeKind::Instance => {
242
+ let visibility = match attribute_visibility {
243
+ AttributeVisibility::Public => Visibility::Public,
244
+ AttributeVisibility::Private => Visibility::Private,
245
+ AttributeVisibility::Unspecified => self.current_visibility,
246
+ };
247
+ (visibility, None)
248
+ }
249
+ AttributeKind::Singleton => {
250
+ let visibility = match attribute_visibility {
251
+ AttributeVisibility::Private => Visibility::Private,
252
+ AttributeVisibility::Public | AttributeVisibility::Unspecified => Visibility::Public,
253
+ };
254
+ (
255
+ visibility,
256
+ Some(Receiver::SelfReceiver(
257
+ lexical_nesting_id.expect("Singleton attribute must have a lexical enclosing scope"),
258
+ )),
259
+ )
260
+ }
261
+ };
262
+
263
+ match (reader, writer) {
264
+ (true, true) => {
265
+ self.register_attribute_method(
266
+ name,
267
+ false,
268
+ offset.clone(),
269
+ name_offset.clone(),
270
+ comments.clone(),
271
+ flags.clone(),
272
+ lexical_nesting_id,
273
+ visibility,
274
+ receiver.clone(),
275
+ );
276
+ self.register_attribute_method(
277
+ name,
278
+ true,
279
+ offset,
280
+ name_offset,
281
+ comments,
282
+ flags,
283
+ lexical_nesting_id,
284
+ visibility,
285
+ receiver,
286
+ );
287
+ }
288
+ (true, false) => self.register_attribute_method(
289
+ name,
290
+ false,
291
+ offset,
292
+ name_offset,
293
+ comments,
294
+ flags,
295
+ lexical_nesting_id,
296
+ visibility,
297
+ receiver,
298
+ ),
299
+ (false, true) => self.register_attribute_method(
300
+ name,
301
+ true,
302
+ offset,
303
+ name_offset,
304
+ comments,
305
+ flags,
306
+ lexical_nesting_id,
307
+ visibility,
308
+ receiver,
309
+ ),
310
+ (false, false) => unreachable!("attribute must have a reader or writer"),
311
+ }
312
+ }
313
+
314
+ #[allow(clippy::too_many_arguments)]
315
+ fn register_attribute_method(
316
+ &mut self,
317
+ name: &str,
318
+ writer: bool,
319
+ offset: Offset,
320
+ name_offset: Offset,
321
+ comments: Box<[Comment]>,
322
+ flags: DefinitionFlags,
323
+ lexical_nesting_id: Option<DefinitionId>,
324
+ visibility: Visibility,
325
+ receiver: Option<Receiver>,
326
+ ) {
327
+ let str_id = self
328
+ .local_graph
329
+ .intern_string(format!("{name}{}()", if writer { "=" } else { "" }));
330
+ let signatures = if writer {
331
+ let parameter_name = self.local_graph.intern_string(name.to_owned());
332
+ let parameter = Parameter::RequiredPositional(ParameterStruct::new(name_offset.clone(), parameter_name));
333
+ Signatures::Simple(vec![parameter].into_boxed_slice())
334
+ } else {
335
+ Signatures::Simple(Box::new([]))
336
+ };
337
+
338
+ let definition = Definition::Method(Box::new(MethodDefinition::new(
339
+ str_id,
340
+ self.uri_id,
341
+ offset,
342
+ name_offset,
343
+ comments,
344
+ flags,
345
+ lexical_nesting_id,
346
+ signatures,
347
+ visibility,
348
+ receiver,
349
+ )));
350
+ self.register_definition(definition, lexical_nesting_id);
351
+ }
352
+
226
353
  #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
227
354
  fn source_at(&self, location: &node::RBSLocationRange) -> String {
228
355
  let start = location.start() as usize;
@@ -574,6 +701,51 @@ impl Visit for RBSIndexer<'_> {
574
701
  self.register_definition(definition, lexical_nesting_id);
575
702
  }
576
703
 
704
+ fn visit_attr_reader_node(&mut self, attribute_node: &AttrReaderNode) {
705
+ self.register_attribute_methods(
706
+ attribute_node.name().as_str(),
707
+ Offset::from_rbs_location(&attribute_node.location()),
708
+ Offset::from_rbs_location(&attribute_node.name_location()),
709
+ self.collect_comments(attribute_node.comment()),
710
+ Self::flags(&attribute_node.annotations()),
711
+ self.parent_lexical_scope_id(),
712
+ attribute_node.kind(),
713
+ attribute_node.visibility(),
714
+ true,
715
+ false,
716
+ );
717
+ }
718
+
719
+ fn visit_attr_writer_node(&mut self, attribute_node: &AttrWriterNode) {
720
+ self.register_attribute_methods(
721
+ attribute_node.name().as_str(),
722
+ Offset::from_rbs_location(&attribute_node.location()),
723
+ Offset::from_rbs_location(&attribute_node.name_location()),
724
+ self.collect_comments(attribute_node.comment()),
725
+ Self::flags(&attribute_node.annotations()),
726
+ self.parent_lexical_scope_id(),
727
+ attribute_node.kind(),
728
+ attribute_node.visibility(),
729
+ false,
730
+ true,
731
+ );
732
+ }
733
+
734
+ fn visit_attr_accessor_node(&mut self, attribute_node: &AttrAccessorNode) {
735
+ self.register_attribute_methods(
736
+ attribute_node.name().as_str(),
737
+ Offset::from_rbs_location(&attribute_node.location()),
738
+ Offset::from_rbs_location(&attribute_node.name_location()),
739
+ self.collect_comments(attribute_node.comment()),
740
+ Self::flags(&attribute_node.annotations()),
741
+ self.parent_lexical_scope_id(),
742
+ attribute_node.kind(),
743
+ attribute_node.visibility(),
744
+ true,
745
+ true,
746
+ );
747
+ }
748
+
577
749
  fn visit_method_definition_node(&mut self, def_node: &node::MethodDefinitionNode) {
578
750
  let str_id = self.local_graph.intern_string(format!("{}()", def_node.name()));
579
751
  let offset = Offset::from_rbs_location(&def_node.location());
@@ -651,6 +823,7 @@ impl Visit for RBSIndexer<'_> {
651
823
  mod tests {
652
824
  use ruby_rbs::node::{self, Node, NodeList};
653
825
 
826
+ use crate::diagnostic::Severity;
654
827
  use crate::indexing::rbs_indexer::RBSIndexer;
655
828
  use crate::model::definitions::{Definition, DefinitionFlags, Parameter, Signatures};
656
829
  use crate::model::visibility::Visibility;
@@ -682,7 +855,11 @@ mod tests {
682
855
  fn index_source_with_errors() {
683
856
  let context = index_source("module");
684
857
 
685
- assert_local_diagnostics_eq!(&context, ["parse-error: Failed to parse RBS document (1:1-1:1)"]);
858
+ assert_local_diagnostics_eq!(
859
+ &context,
860
+ ["ParseError: Failed to parse RBS document (1:1-1:1)"],
861
+ severity: Severity::Error
862
+ );
686
863
 
687
864
  assert!(context.graph().definitions().is_empty());
688
865
  }
@@ -1051,6 +1228,93 @@ mod tests {
1051
1228
  });
1052
1229
  }
1053
1230
 
1231
+ #[test]
1232
+ fn indexes_attribute_members_as_methods_without_retaining_types_or_instance_variables() {
1233
+ let context = index_source({
1234
+ "
1235
+ class Foo
1236
+ # Reader documentation
1237
+ %a{deprecated}
1238
+ attr_reader inferred: Integer
1239
+ attr_reader absent(): Symbol
1240
+ attr_writer explicit (@writer): String
1241
+ attr_accessor accessor: bool
1242
+ private
1243
+ attr_reader inherited_visibility: Float
1244
+ public
1245
+ private attr_accessor self.class_value (@class_value): bool
1246
+ end
1247
+ "
1248
+ });
1249
+
1250
+ assert_no_local_diagnostics!(&context);
1251
+ assert_eq!(context.graph().definitions().len(), 9);
1252
+
1253
+ let method = |name: &str| {
1254
+ context
1255
+ .graph()
1256
+ .definitions()
1257
+ .values()
1258
+ .find_map(|definition| match definition {
1259
+ Definition::Method(method)
1260
+ if context
1261
+ .graph()
1262
+ .strings()
1263
+ .get(method.str_id())
1264
+ .is_some_and(|string| string.as_str() == name) =>
1265
+ {
1266
+ Some(method)
1267
+ }
1268
+ _ => None,
1269
+ })
1270
+ .unwrap_or_else(|| panic!("expected `{name}` method definition"))
1271
+ };
1272
+
1273
+ for name in [
1274
+ "inferred()",
1275
+ "absent()",
1276
+ "explicit=()",
1277
+ "accessor()",
1278
+ "accessor=()",
1279
+ "inherited_visibility()",
1280
+ "class_value()",
1281
+ "class_value=()",
1282
+ ] {
1283
+ assert_eq!(method(name).signatures().as_slice().len(), 1);
1284
+ }
1285
+
1286
+ for name in [
1287
+ "inferred()",
1288
+ "absent()",
1289
+ "accessor()",
1290
+ "inherited_visibility()",
1291
+ "class_value()",
1292
+ ] {
1293
+ assert!(method(name).signatures().as_slice()[0].is_empty());
1294
+ }
1295
+
1296
+ for (name, parameter_name) in [
1297
+ ("explicit=()", "explicit"),
1298
+ ("accessor=()", "accessor"),
1299
+ ("class_value=()", "class_value"),
1300
+ ] {
1301
+ let signature = &method(name).signatures().as_slice()[0];
1302
+ let [Parameter::RequiredPositional(parameter)] = signature.as_ref() else {
1303
+ panic!("expected `{name}` to have one required positional parameter");
1304
+ };
1305
+ assert_string_eq!(&context, parameter.str(), parameter_name);
1306
+ assert_offset_string!(&context, parameter.offset(), parameter_name);
1307
+ }
1308
+
1309
+ assert_eq!(method("class_value()").visibility(), &Visibility::Private);
1310
+ assert_eq!(method("class_value=()").visibility(), &Visibility::Private);
1311
+ assert_eq!(method("inherited_visibility()").visibility(), &Visibility::Private);
1312
+ assert_method_has_receiver!(&context, method("class_value()"), "Foo");
1313
+ assert_method_has_receiver!(&context, method("class_value=()"), "Foo");
1314
+ assert_def_comments_eq!(&context, method("inferred()"), ["# Reader documentation"]);
1315
+ assert!(method("inferred()").flags().contains(DefinitionFlags::DEPRECATED));
1316
+ }
1317
+
1054
1318
  #[test]
1055
1319
  fn index_alias_node() {
1056
1320
  let context = index_source({
@@ -12,7 +12,7 @@ use crate::model::definitions::{
12
12
  };
13
13
  use crate::model::document::Document;
14
14
  use crate::model::ids::{DefinitionId, NameId, StringId, UriId};
15
- use crate::model::name::{Name, ParentScope};
15
+ use crate::model::name::ParentScope;
16
16
  use crate::model::references::{ConstantReference, MethodRef};
17
17
  use crate::model::visibility::Visibility;
18
18
  use crate::offset::Offset;
@@ -482,11 +482,9 @@ impl<'a> RubyIndexer<'a> {
482
482
  let offset = Offset::from_prism_location(&location);
483
483
  let name = Self::location_to_string(&location);
484
484
  let string_id = self.local_graph.intern_string(name);
485
- let name_id = self.local_graph.add_name(Name::new(
486
- string_id,
487
- parent_scope_id,
488
- self.current_lexical_scope_name_id(),
489
- ));
485
+ let name_id = self
486
+ .local_graph
487
+ .add_name(string_id, parent_scope_id, self.current_lexical_scope_name_id());
490
488
 
491
489
  if push_final_reference {
492
490
  self.local_graph
@@ -687,7 +685,7 @@ impl<'a> RubyIndexer<'a> {
687
685
  .intern_string(format!("{}:{}<anonymous>", self.uri_id, offset.start()));
688
686
 
689
687
  (
690
- Some(self.local_graph.add_name(Name::new(string_id, ParentScope::None, None))),
688
+ Some(self.local_graph.add_name(string_id, ParentScope::None, None)),
691
689
  offset.clone(),
692
690
  )
693
691
  };
@@ -748,7 +746,7 @@ impl<'a> RubyIndexer<'a> {
748
746
  .intern_string(format!("{}:{}<anonymous>", self.uri_id, offset.start()));
749
747
 
750
748
  (
751
- Some(self.local_graph.add_name(Name::new(string_id, ParentScope::None, None))),
749
+ Some(self.local_graph.add_name(string_id, ParentScope::None, None)),
752
750
  offset.clone(),
753
751
  )
754
752
  };
@@ -969,7 +967,7 @@ impl<'a> RubyIndexer<'a> {
969
967
  }
970
968
 
971
969
  Some((
972
- self.current_lexical_scope_name_id().unwrap(),
970
+ self.current_owner_name_id().unwrap(),
973
971
  Offset::from_prism_location(&arg.location()),
974
972
  ))
975
973
  } else if let Some(name_id) = self.index_constant_reference(&arg, false) {
@@ -1125,7 +1123,7 @@ impl<'a> RubyIndexer<'a> {
1125
1123
  }
1126
1124
  None => {
1127
1125
  let str_id = self.local_graph.intern_string("Object".into());
1128
- Some(self.local_graph.add_name(Name::new(str_id, ParentScope::None, None)))
1126
+ Some(self.local_graph.add_name(str_id, ParentScope::None, None))
1129
1127
  }
1130
1128
  }
1131
1129
  }
@@ -1169,7 +1167,7 @@ impl<'a> RubyIndexer<'a> {
1169
1167
  let string_id = self.local_graph.intern_string(singleton_class_name);
1170
1168
  let new_name_id = self
1171
1169
  .local_graph
1172
- .add_name(Name::new(string_id, ParentScope::Attached(name_id), None));
1170
+ .add_name(string_id, ParentScope::Attached(name_id), None);
1173
1171
 
1174
1172
  let location = receiver.map_or(fallback_location, ruby_prism::Node::location);
1175
1173
  let offset = Offset::from_prism_location(&location);
@@ -1650,7 +1648,7 @@ impl Visit<'_> for RubyIndexer<'_> {
1650
1648
  let nesting = self.current_lexical_scope_name_id();
1651
1649
  let name_id = self
1652
1650
  .local_graph
1653
- .add_name(Name::new(string_id, ParentScope::Attached(attached_target), nesting));
1651
+ .add_name(string_id, ParentScope::Attached(attached_target), nesting);
1654
1652
 
1655
1653
  let definition = Definition::SingletonClass(Box::new(SingletonClassDefinition::new(
1656
1654
  name_id,