rubydex 0.2.9 → 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.
- checksums.yaml +4 -4
- data/README.md +110 -6
- data/THIRD_PARTY_LICENSES.html +271 -2
- data/exe/rdx +2 -73
- data/ext/rubydex/config.c +140 -0
- data/ext/rubydex/config.h +16 -0
- data/ext/rubydex/declaration.c +1 -1
- data/ext/rubydex/definition.c +32 -4
- data/ext/rubydex/diagnostic.c +75 -1
- data/ext/rubydex/diagnostic.h +2 -0
- data/ext/rubydex/graph.c +27 -48
- data/ext/rubydex/graph.h +6 -0
- data/ext/rubydex/query.c +487 -0
- data/ext/rubydex/query.h +8 -0
- data/ext/rubydex/reference.c +60 -0
- data/ext/rubydex/rubydex.c +4 -0
- data/ext/rubydex/utils.c +23 -4
- data/ext/rubydex/utils.h +5 -0
- data/lib/ruby_lsp/rubydex/addon.rb +211 -0
- data/lib/rubydex/cli/command/console.rb +55 -0
- data/lib/rubydex/cli/command/lint/explain.rb +74 -0
- data/lib/rubydex/cli/command/lint.rb +202 -0
- data/lib/rubydex/cli/command/mcp.rb +30 -0
- data/lib/rubydex/cli/command/query.rb +70 -0
- data/lib/rubydex/cli/command/skill.rb +69 -0
- data/lib/rubydex/cli/command.rb +168 -0
- data/lib/rubydex/cli.rb +93 -0
- data/lib/rubydex/config.rb +59 -0
- data/lib/rubydex/diagnostic.rb +12 -3
- data/lib/rubydex/errors.rb +42 -1
- data/lib/rubydex/graph.rb +10 -3
- data/lib/rubydex/linter/custom_rule.rb +97 -0
- data/lib/rubydex/linter/helpers/path_helpers.rb +78 -0
- data/lib/rubydex/linter/helpers/source_access_helpers.rb +31 -0
- data/lib/rubydex/linter/rule_loader.rb +36 -0
- data/lib/rubydex/linter/rule_test_case.rb +343 -0
- data/lib/rubydex/linter/runner.rb +56 -0
- data/lib/rubydex/linter.rb +19 -0
- data/lib/rubydex/location.rb +3 -0
- data/lib/rubydex/mcp_server.rb +1 -2
- data/lib/rubydex/related_information.rb +17 -0
- data/lib/rubydex/rule.rb +33 -0
- data/lib/rubydex/severity.rb +70 -0
- data/lib/rubydex/skill.rb +88 -0
- data/lib/rubydex/skill_registry.rb +62 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +6 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +125 -0
- data/rbi/rubydex.rbi +578 -15
- data/rust/Cargo.lock +7 -0
- data/rust/rubydex/Cargo.toml +1 -0
- data/rust/rubydex/benches/graph_memory.rs +20 -4
- data/rust/rubydex/src/compile_assertions.rs +15 -0
- data/rust/rubydex/src/config.rs +538 -157
- data/rust/rubydex/src/diagnostic.rs +66 -40
- data/rust/rubydex/src/errors.rs +0 -1
- data/rust/rubydex/src/indexing/local_graph.rs +6 -5
- data/rust/rubydex/src/indexing/rbs_indexer.rs +284 -8
- data/rust/rubydex/src/indexing/ruby_indexer.rs +59 -70
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +195 -86
- data/rust/rubydex/src/lib.rs +1 -0
- data/rust/rubydex/src/listing.rs +26 -1
- data/rust/rubydex/src/main.rs +9 -128
- data/rust/rubydex/src/model/declaration.rs +301 -229
- data/rust/rubydex/src/model/definitions.rs +27 -26
- data/rust/rubydex/src/model/document.rs +43 -7
- data/rust/rubydex/src/model/graph.rs +67 -68
- data/rust/rubydex/src/model/id.rs +55 -0
- data/rust/rubydex/src/model/ids.rs +21 -9
- data/rust/rubydex/src/model/name.rs +88 -19
- data/rust/rubydex/src/model/references.rs +16 -13
- data/rust/rubydex/src/operation/ruby_builder.rs +78 -104
- data/rust/rubydex/src/path_helpers.rs +77 -0
- data/rust/rubydex/src/query/cypher/schema.rs +853 -0
- data/rust/rubydex/src/query/cypher/schema_info.rs +161 -0
- data/rust/rubydex/src/query/cypher/tests.rs +253 -0
- data/rust/rubydex/src/query/cypher.rs +54 -0
- data/rust/rubydex/src/query.rs +125 -43
- data/rust/rubydex/src/resolution.rs +368 -395
- data/rust/rubydex/src/resolution_tests.rs +504 -78
- data/rust/rubydex/src/test_utils/context.rs +2 -1
- data/rust/rubydex/src/test_utils/graph_test.rs +26 -12
- data/rust/rubydex/src/test_utils/local_graph_test.rs +19 -0
- data/rust/rubydex/tests/cli.rs +4 -4
- data/rust/rubydex-sys/src/config_api.rs +205 -0
- data/rust/rubydex-sys/src/cypher_api.rs +791 -0
- data/rust/rubydex-sys/src/declaration_api.rs +6 -3
- data/rust/rubydex-sys/src/definition_api.rs +27 -7
- data/rust/rubydex-sys/src/diagnostic_api.rs +77 -8
- data/rust/rubydex-sys/src/graph_api.rs +31 -68
- data/rust/rubydex-sys/src/lib.rs +2 -0
- data/rust/rubydex-sys/src/name_api.rs +2 -6
- data/rust/rubydex-sys/src/reference_api.rs +58 -12
- data/rust/rubydex-sys/src/utils.rs +37 -0
- data/skills/send-private-method/SKILL.md +133 -0
- metadata +37 -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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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
|
}
|
data/rust/rubydex/src/errors.rs
CHANGED
|
@@ -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,
|
|
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(&
|
|
132
|
+
if let Some(&parent_scope_id) = parent_scope.as_ref() {
|
|
132
133
|
self.name_dependents
|
|
133
|
-
.entry(
|
|
134
|
+
.entry(parent_scope_id)
|
|
134
135
|
.or_default()
|
|
135
136
|
.push(NameDependent::ChildName(name_id));
|
|
136
137
|
}
|
|
137
|
-
if let Some(
|
|
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,
|
|
7
|
-
|
|
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::
|
|
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;
|
|
@@ -80,7 +81,16 @@ impl<'a> RBSIndexer<'a> {
|
|
|
80
81
|
let Node::Symbol(symbol) = path_node else {
|
|
81
82
|
continue;
|
|
82
83
|
};
|
|
83
|
-
|
|
84
|
+
let name_id = self.intern_name(&symbol, parent_scope, nesting_name_id);
|
|
85
|
+
|
|
86
|
+
// Emit a constant reference for each parent-scope segment so it gets its own resolution
|
|
87
|
+
// unit, mirroring the Ruby indexer (see `index_constant_reference`). Otherwise the
|
|
88
|
+
// parent scope is an orphan name that never resolves, leaving qualified references stuck.
|
|
89
|
+
let offset = Offset::from_rbs_location(&symbol.location());
|
|
90
|
+
self.local_graph
|
|
91
|
+
.add_constant_reference(ConstantReference::new(name_id, self.uri_id, offset));
|
|
92
|
+
|
|
93
|
+
parent_scope = ParentScope::Some(name_id);
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
self.intern_name(&type_name.name(), parent_scope, nesting_name_id)
|
|
@@ -93,8 +103,7 @@ impl<'a> RBSIndexer<'a> {
|
|
|
93
103
|
nesting_name_id: Option<NameId>,
|
|
94
104
|
) -> NameId {
|
|
95
105
|
let string_id = self.local_graph.intern_string(symbol.as_str().to_owned());
|
|
96
|
-
self.local_graph
|
|
97
|
-
.add_name(Name::new(string_id, parent_scope, nesting_name_id))
|
|
106
|
+
self.local_graph.add_name(string_id, parent_scope, nesting_name_id)
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
fn parent_lexical_scope_id(&self) -> Option<DefinitionId> {
|
|
@@ -214,6 +223,133 @@ impl<'a> RBSIndexer<'a> {
|
|
|
214
223
|
definition_id
|
|
215
224
|
}
|
|
216
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
|
+
|
|
217
353
|
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
|
218
354
|
fn source_at(&self, location: &node::RBSLocationRange) -> String {
|
|
219
355
|
let start = location.start() as usize;
|
|
@@ -486,7 +622,10 @@ impl Visit for RBSIndexer<'_> {
|
|
|
486
622
|
self.uri_id,
|
|
487
623
|
offset,
|
|
488
624
|
comments,
|
|
489
|
-
|
|
625
|
+
// RBS establishes that the constant exists, but its value type is intentionally not
|
|
626
|
+
// represented in the graph. Treat it like a dynamic Ruby assignment so resolution
|
|
627
|
+
// may promote it when a namespace or singleton receiver is required.
|
|
628
|
+
Self::flags(&constant_node.annotations()) | DefinitionFlags::PROMOTABLE,
|
|
490
629
|
lexical_nesting_id,
|
|
491
630
|
)));
|
|
492
631
|
|
|
@@ -562,6 +701,51 @@ impl Visit for RBSIndexer<'_> {
|
|
|
562
701
|
self.register_definition(definition, lexical_nesting_id);
|
|
563
702
|
}
|
|
564
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
|
+
|
|
565
749
|
fn visit_method_definition_node(&mut self, def_node: &node::MethodDefinitionNode) {
|
|
566
750
|
let str_id = self.local_graph.intern_string(format!("{}()", def_node.name()));
|
|
567
751
|
let offset = Offset::from_rbs_location(&def_node.location());
|
|
@@ -639,6 +823,7 @@ impl Visit for RBSIndexer<'_> {
|
|
|
639
823
|
mod tests {
|
|
640
824
|
use ruby_rbs::node::{self, Node, NodeList};
|
|
641
825
|
|
|
826
|
+
use crate::diagnostic::Severity;
|
|
642
827
|
use crate::indexing::rbs_indexer::RBSIndexer;
|
|
643
828
|
use crate::model::definitions::{Definition, DefinitionFlags, Parameter, Signatures};
|
|
644
829
|
use crate::model::visibility::Visibility;
|
|
@@ -670,7 +855,11 @@ mod tests {
|
|
|
670
855
|
fn index_source_with_errors() {
|
|
671
856
|
let context = index_source("module");
|
|
672
857
|
|
|
673
|
-
assert_local_diagnostics_eq!(
|
|
858
|
+
assert_local_diagnostics_eq!(
|
|
859
|
+
&context,
|
|
860
|
+
["ParseError: Failed to parse RBS document (1:1-1:1)"],
|
|
861
|
+
severity: Severity::Error
|
|
862
|
+
);
|
|
674
863
|
|
|
675
864
|
assert!(context.graph().definitions().is_empty());
|
|
676
865
|
}
|
|
@@ -1039,6 +1228,93 @@ mod tests {
|
|
|
1039
1228
|
});
|
|
1040
1229
|
}
|
|
1041
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
|
+
|
|
1042
1318
|
#[test]
|
|
1043
1319
|
fn index_alias_node() {
|
|
1044
1320
|
let context = index_source({
|