rubydex 0.2.5 → 0.2.6
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 +17 -16
- data/THIRD_PARTY_LICENSES.html +6 -6
- data/ext/rubydex/definition.c +33 -2
- data/ext/rubydex/document.c +36 -0
- data/ext/rubydex/graph.c +32 -18
- data/ext/rubydex/handle.h +21 -5
- data/lib/rubydex/bin/rubydex_mcp.exe +0 -0
- data/lib/rubydex/errors.rb +8 -0
- data/lib/rubydex/location.rb +24 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +1 -0
- data/rbi/rubydex.rbi +29 -12
- data/rust/Cargo.lock +3 -3
- data/rust/rubydex/Cargo.toml +7 -1
- data/rust/rubydex/src/dot.rs +609 -0
- data/rust/rubydex/src/indexing/rbs_indexer.rs +19 -1
- data/rust/rubydex/src/indexing/ruby_indexer.rs +4 -0
- data/rust/rubydex/src/lib.rs +1 -1
- data/rust/rubydex/src/main.rs +8 -5
- data/rust/rubydex/src/model/built_in.rs +5 -2
- data/rust/rubydex/src/model/comment.rs +2 -0
- data/rust/rubydex/src/model/declaration.rs +1 -0
- data/rust/rubydex/src/model/definitions.rs +13 -1
- data/rust/rubydex/src/model/document.rs +2 -0
- data/rust/rubydex/src/model/encoding.rs +2 -0
- data/rust/rubydex/src/model/graph.rs +51 -13
- data/rust/rubydex/src/model/identity_maps.rs +3 -0
- data/rust/rubydex/src/model/keywords.rs +3 -0
- data/rust/rubydex/src/model/name.rs +2 -0
- data/rust/rubydex/src/model/string_ref.rs +2 -0
- data/rust/rubydex/src/model/visibility.rs +3 -0
- data/rust/rubydex/src/operation/applier.rs +1 -0
- data/rust/rubydex/src/operation/mod.rs +1 -0
- data/rust/rubydex/src/operation/ruby_builder.rs +4 -0
- data/rust/rubydex/src/query.rs +114 -33
- data/rust/rubydex/src/resolution.rs +16 -8
- data/rust/rubydex/src/resolution_tests.rs +132 -0
- data/rust/rubydex/tests/cli.rs +17 -61
- data/rust/rubydex-mcp/Cargo.toml +9 -3
- data/rust/rubydex-sys/Cargo.toml +9 -2
- data/rust/rubydex-sys/src/definition_api.rs +72 -2
- data/rust/rubydex-sys/src/document_api.rs +28 -0
- data/rust/rubydex-sys/src/graph_api.rs +1 -3
- metadata +4 -4
- data/rust/rubydex/src/visualization/dot.rs +0 -192
- data/rust/rubydex/src/visualization.rs +0 -6
|
@@ -7,6 +7,7 @@ use crate::reference_api::CConstantReference;
|
|
|
7
7
|
use libc::c_char;
|
|
8
8
|
use rubydex::model::definitions::{Definition, Mixin};
|
|
9
9
|
use rubydex::model::ids::DefinitionId;
|
|
10
|
+
use rubydex::query::AliasResolutionError;
|
|
10
11
|
use std::ffi::CString;
|
|
11
12
|
use std::ptr;
|
|
12
13
|
|
|
@@ -346,8 +347,8 @@ pub unsafe extern "C" fn rdx_definition_is_deprecated(pointer: GraphPointer, def
|
|
|
346
347
|
}
|
|
347
348
|
|
|
348
349
|
/// Returns a newly allocated `Location` for the name portion of a definition id.
|
|
349
|
-
/// For class, module,
|
|
350
|
-
/// the name (e.g., "Bar" in `class Foo::Bar`).
|
|
350
|
+
/// For class, module, singleton class, and method definitions, this returns the location of just
|
|
351
|
+
/// the name (e.g., "Bar" in `class Foo::Bar`, or "foo" in `def foo`).
|
|
351
352
|
/// For other definition types, returns NULL.
|
|
352
353
|
/// Caller must free the returned pointer with `rdx_location_free`.
|
|
353
354
|
///
|
|
@@ -491,3 +492,72 @@ pub unsafe extern "C" fn rdx_definition_mixins(pointer: GraphPointer, definition
|
|
|
491
492
|
MixinsIter::new(entries.into_boxed_slice())
|
|
492
493
|
})
|
|
493
494
|
}
|
|
495
|
+
|
|
496
|
+
/// Status of a `MethodAliasDefinition#target` resolution.
|
|
497
|
+
#[repr(u8)]
|
|
498
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
499
|
+
pub enum CMethodAliasResolution {
|
|
500
|
+
/// The alias chain resolved successfully; `declaration` is valid.
|
|
501
|
+
Resolved = 0,
|
|
502
|
+
/// The chain could not be resolved because the target name does not exist on the owner, or the owner itself was
|
|
503
|
+
/// never resolved. Treated as `nil` on the Ruby side.
|
|
504
|
+
NotFound = 1,
|
|
505
|
+
/// The alias chain forms a cycle. Surfaced as a `Rubydex::AliasCycleError` on the Ruby side.
|
|
506
|
+
Cycle = 2,
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
#[repr(C)]
|
|
510
|
+
#[derive(Debug)]
|
|
511
|
+
pub struct CMethodAliasTargetResult {
|
|
512
|
+
pub status: CMethodAliasResolution,
|
|
513
|
+
pub declaration: *const CDeclaration,
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/// Resolves a `MethodAliasDefinition` to its target method declaration via `query::follow_method_alias` and reports the
|
|
517
|
+
/// outcome as a tagged status. The `declaration` pointer is non-null only when `status == Resolved`; the caller is
|
|
518
|
+
/// responsible for freeing it with `free_c_declaration`.
|
|
519
|
+
///
|
|
520
|
+
/// # Safety
|
|
521
|
+
/// - `pointer` must be a valid pointer previously returned by `rdx_graph_new`.
|
|
522
|
+
/// - `definition_id` must be a valid definition id for a `MethodAliasDefinition`.
|
|
523
|
+
///
|
|
524
|
+
/// # Panics
|
|
525
|
+
/// Panics on graph inconsistencies (the definition is not a method alias, or the alias resolved to a non-method
|
|
526
|
+
/// declaration).
|
|
527
|
+
#[unsafe(no_mangle)]
|
|
528
|
+
pub unsafe extern "C" fn rdx_method_alias_definition_target(
|
|
529
|
+
pointer: GraphPointer,
|
|
530
|
+
definition_id: u64,
|
|
531
|
+
) -> CMethodAliasTargetResult {
|
|
532
|
+
with_graph(pointer, |graph| {
|
|
533
|
+
let def_id = DefinitionId::new(definition_id);
|
|
534
|
+
|
|
535
|
+
match rubydex::query::follow_method_alias(graph, def_id) {
|
|
536
|
+
Ok(target_id) => {
|
|
537
|
+
let target_decl = graph
|
|
538
|
+
.declarations()
|
|
539
|
+
.get(&target_id)
|
|
540
|
+
.expect("target declaration must exist");
|
|
541
|
+
let boxed = Box::new(CDeclaration::from_declaration(target_id, target_decl));
|
|
542
|
+
|
|
543
|
+
CMethodAliasTargetResult {
|
|
544
|
+
status: CMethodAliasResolution::Resolved,
|
|
545
|
+
declaration: Box::into_raw(boxed).cast_const(),
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
Err(AliasResolutionError::TargetNotFound | AliasResolutionError::UnresolvedOwner) => {
|
|
549
|
+
CMethodAliasTargetResult {
|
|
550
|
+
status: CMethodAliasResolution::NotFound,
|
|
551
|
+
declaration: ptr::null(),
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
Err(AliasResolutionError::Cycle) => CMethodAliasTargetResult {
|
|
555
|
+
status: CMethodAliasResolution::Cycle,
|
|
556
|
+
declaration: ptr::null(),
|
|
557
|
+
},
|
|
558
|
+
Err(err @ (AliasResolutionError::NotAnAlias | AliasResolutionError::TargetNotMethod)) => {
|
|
559
|
+
panic!("graph inconsistency in method alias resolution: {err:?}")
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
})
|
|
563
|
+
}
|
|
@@ -6,6 +6,7 @@ use std::ptr;
|
|
|
6
6
|
|
|
7
7
|
use crate::definition_api::{DefinitionsIter, rdx_definitions_iter_new_from_ids};
|
|
8
8
|
use crate::graph_api::{GraphPointer, with_graph};
|
|
9
|
+
use crate::reference_api::{CMethodReference, MethodReferencesIter};
|
|
9
10
|
use rubydex::model::ids::UriId;
|
|
10
11
|
|
|
11
12
|
#[derive(Debug)]
|
|
@@ -83,3 +84,30 @@ pub unsafe extern "C" fn rdx_document_definitions_iter_new(pointer: GraphPointer
|
|
|
83
84
|
}
|
|
84
85
|
})
|
|
85
86
|
}
|
|
87
|
+
|
|
88
|
+
/// Creates a new iterator over method reference IDs for a given document by snapshotting the current set of IDs.
|
|
89
|
+
///
|
|
90
|
+
/// # Safety
|
|
91
|
+
///
|
|
92
|
+
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
93
|
+
/// - The returned pointer must be freed with `rdx_method_references_iter_free`.
|
|
94
|
+
#[unsafe(no_mangle)]
|
|
95
|
+
pub unsafe extern "C" fn rdx_document_method_references_iter_new(
|
|
96
|
+
pointer: GraphPointer,
|
|
97
|
+
uri_id: u64,
|
|
98
|
+
) -> *mut MethodReferencesIter {
|
|
99
|
+
with_graph(pointer, |graph| {
|
|
100
|
+
let uri_id = UriId::new(uri_id);
|
|
101
|
+
if let Some(doc) = graph.documents().get(&uri_id) {
|
|
102
|
+
let entries: Vec<_> = doc
|
|
103
|
+
.method_references()
|
|
104
|
+
.iter()
|
|
105
|
+
.map(|ref_id| CMethodReference { id: **ref_id })
|
|
106
|
+
.collect();
|
|
107
|
+
|
|
108
|
+
MethodReferencesIter::new(entries.into_boxed_slice())
|
|
109
|
+
} else {
|
|
110
|
+
MethodReferencesIter::new(Vec::<_>::new().into_boxed_slice())
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
}
|
|
@@ -793,9 +793,7 @@ fn run_and_finalize_completion(
|
|
|
793
793
|
///
|
|
794
794
|
/// - `pointer` must be a valid `GraphPointer` previously returned by this crate.
|
|
795
795
|
/// - `nesting` must point to `nesting_count` valid, null-terminated UTF-8 strings.
|
|
796
|
-
/// - `self_receiver`
|
|
797
|
-
/// overrides the self-type (e.g., `"Foo::<Foo>"` for completion inside `def Foo.bar`), while
|
|
798
|
-
/// the lexical nesting still comes from `nesting`.
|
|
796
|
+
/// - `self_receiver` is the fully qualified name of the **type of `self`**
|
|
799
797
|
#[unsafe(no_mangle)]
|
|
800
798
|
pub unsafe extern "C" fn rdx_graph_complete_expression(
|
|
801
799
|
pointer: GraphPointer,
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubydex
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A high-performance static analysis suite for Ruby, built in Rust with
|
|
14
14
|
Ruby APIs
|
|
@@ -52,6 +52,7 @@ files:
|
|
|
52
52
|
- lib/rubydex/comment.rb
|
|
53
53
|
- lib/rubydex/declaration.rb
|
|
54
54
|
- lib/rubydex/diagnostic.rb
|
|
55
|
+
- lib/rubydex/errors.rb
|
|
55
56
|
- lib/rubydex/failures.rb
|
|
56
57
|
- lib/rubydex/graph.rb
|
|
57
58
|
- lib/rubydex/keyword.rb
|
|
@@ -89,6 +90,7 @@ files:
|
|
|
89
90
|
- rust/rubydex/Cargo.toml
|
|
90
91
|
- rust/rubydex/src/compile_assertions.rs
|
|
91
92
|
- rust/rubydex/src/diagnostic.rs
|
|
93
|
+
- rust/rubydex/src/dot.rs
|
|
92
94
|
- rust/rubydex/src/errors.rs
|
|
93
95
|
- rust/rubydex/src/indexing.rs
|
|
94
96
|
- rust/rubydex/src/indexing/local_graph.rs
|
|
@@ -133,8 +135,6 @@ files:
|
|
|
133
135
|
- rust/rubydex/src/test_utils/context.rs
|
|
134
136
|
- rust/rubydex/src/test_utils/graph_test.rs
|
|
135
137
|
- rust/rubydex/src/test_utils/local_graph_test.rs
|
|
136
|
-
- rust/rubydex/src/visualization.rs
|
|
137
|
-
- rust/rubydex/src/visualization/dot.rs
|
|
138
138
|
- rust/rubydex/tests/cli.rs
|
|
139
139
|
- rust/rustfmt.toml
|
|
140
140
|
homepage: https://github.com/Shopify/rubydex
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
//! DOT format generator for Graphviz visualization of the graph structure.
|
|
2
|
-
|
|
3
|
-
use std::fmt::Write;
|
|
4
|
-
|
|
5
|
-
use crate::model::graph::Graph;
|
|
6
|
-
|
|
7
|
-
const NAME_NODE_SHAPE: &str = "hexagon";
|
|
8
|
-
const DEFINITION_NODE_SHAPE: &str = "ellipse";
|
|
9
|
-
const URI_NODE_SHAPE: &str = "box";
|
|
10
|
-
|
|
11
|
-
/// Escapes a string for use in DOT format labels and identifiers.
|
|
12
|
-
fn escape_dot_string(s: &str) -> String {
|
|
13
|
-
if !s.contains('"') {
|
|
14
|
-
return s.to_string();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
let mut result = String::with_capacity(s.len());
|
|
18
|
-
for c in s.chars() {
|
|
19
|
-
match c {
|
|
20
|
-
'"' => result.push_str("\\\""),
|
|
21
|
-
_ => result.push(c),
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
result
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
#[must_use]
|
|
28
|
-
pub fn generate(graph: &Graph) -> String {
|
|
29
|
-
let mut output = String::new();
|
|
30
|
-
output.push_str("digraph {\n");
|
|
31
|
-
output.push_str(" rankdir=TB;\n\n");
|
|
32
|
-
|
|
33
|
-
write_declaration_nodes(&mut output, graph);
|
|
34
|
-
write_definition_nodes(&mut output, graph);
|
|
35
|
-
write_document_nodes(&mut output, graph);
|
|
36
|
-
|
|
37
|
-
output.push_str("}\n");
|
|
38
|
-
output
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
fn write_declaration_nodes(output: &mut String, graph: &Graph) {
|
|
42
|
-
let mut declarations: Vec<_> = graph.declarations().values().collect();
|
|
43
|
-
declarations.sort_by(|a, b| a.name().cmp(b.name()));
|
|
44
|
-
|
|
45
|
-
for declaration in declarations {
|
|
46
|
-
let name = declaration.name();
|
|
47
|
-
let escaped_name = escape_dot_string(name);
|
|
48
|
-
let node_id = format!("Name:{name}");
|
|
49
|
-
let _ = writeln!(
|
|
50
|
-
output,
|
|
51
|
-
" \"{node_id}\" [label=\"{escaped_name}\",shape={NAME_NODE_SHAPE}];"
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
for def_id in declaration.definitions() {
|
|
55
|
-
let _ = writeln!(output, " \"{node_id}\" -> \"def_{def_id}\" [dir=both];");
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
output.push('\n');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
fn write_definition_nodes(output: &mut String, graph: &Graph) {
|
|
63
|
-
let mut definitions: Vec<_> = graph
|
|
64
|
-
.definitions()
|
|
65
|
-
.iter()
|
|
66
|
-
.filter_map(|(def_id, definition)| {
|
|
67
|
-
graph
|
|
68
|
-
.declarations()
|
|
69
|
-
.get(graph.definition_to_declaration_id(definition).unwrap())
|
|
70
|
-
.map(|declaration| {
|
|
71
|
-
let def_type = definition.kind();
|
|
72
|
-
let escaped_name = escape_dot_string(declaration.name());
|
|
73
|
-
let label = format!("{def_type}({escaped_name})");
|
|
74
|
-
let line = format!(" \"def_{def_id}\" [label=\"{label}\",shape={DEFINITION_NODE_SHAPE}];\n");
|
|
75
|
-
(label, line)
|
|
76
|
-
})
|
|
77
|
-
})
|
|
78
|
-
.collect();
|
|
79
|
-
|
|
80
|
-
definitions.sort_by(|a, b| a.0.cmp(&b.0));
|
|
81
|
-
|
|
82
|
-
for (_, line) in definitions {
|
|
83
|
-
output.push_str(&line);
|
|
84
|
-
}
|
|
85
|
-
output.push('\n');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
fn write_document_nodes(output: &mut String, graph: &Graph) {
|
|
89
|
-
let mut documents: Vec<_> = graph.documents().values().collect();
|
|
90
|
-
documents.sort_by(|a, b| a.uri().cmp(b.uri()));
|
|
91
|
-
|
|
92
|
-
for document in documents {
|
|
93
|
-
let uri = document.uri();
|
|
94
|
-
let label = uri.rsplit('/').next().unwrap_or(uri);
|
|
95
|
-
let escaped_uri = escape_dot_string(uri);
|
|
96
|
-
let escaped_label = escape_dot_string(label);
|
|
97
|
-
let _ = writeln!(
|
|
98
|
-
output,
|
|
99
|
-
" \"{escaped_uri}\" [label=\"{escaped_label}\",shape={URI_NODE_SHAPE}];"
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
for def_id in document.definitions() {
|
|
103
|
-
let _ = writeln!(output, " \"def_{def_id}\" -> \"{escaped_uri}\";");
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
output.push('\n');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
#[cfg(test)]
|
|
110
|
-
mod tests {
|
|
111
|
-
use super::*;
|
|
112
|
-
use crate::{model::ids::DeclarationId, test_utils::GraphTest};
|
|
113
|
-
|
|
114
|
-
fn create_test_graph() -> GraphTest {
|
|
115
|
-
let mut graph_test = GraphTest::new();
|
|
116
|
-
graph_test.index_uri(
|
|
117
|
-
"file:///test.rb",
|
|
118
|
-
"
|
|
119
|
-
class TestClass
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
module TestModule
|
|
123
|
-
end
|
|
124
|
-
",
|
|
125
|
-
);
|
|
126
|
-
graph_test.resolve();
|
|
127
|
-
graph_test
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/// Finds the first definition ID for the declaration with the given name.
|
|
131
|
-
fn def_id_for(graph: &Graph, name: &str) -> String {
|
|
132
|
-
let decl = graph.declarations().get(&DeclarationId::from(name)).unwrap();
|
|
133
|
-
decl.definitions().first().unwrap().to_string()
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
#[test]
|
|
137
|
-
fn test_dot_generation() {
|
|
138
|
-
let context = create_test_graph();
|
|
139
|
-
let dot_output = generate(context.graph());
|
|
140
|
-
|
|
141
|
-
let basic_object_def = def_id_for(context.graph(), "BasicObject");
|
|
142
|
-
let class_def = def_id_for(context.graph(), "Class");
|
|
143
|
-
let kernel_def = def_id_for(context.graph(), "Kernel");
|
|
144
|
-
let module_def = def_id_for(context.graph(), "Module");
|
|
145
|
-
let object_def = def_id_for(context.graph(), "Object");
|
|
146
|
-
let test_class_def = def_id_for(context.graph(), "TestClass");
|
|
147
|
-
let test_module_def = def_id_for(context.graph(), "TestModule");
|
|
148
|
-
|
|
149
|
-
let expected = format!(
|
|
150
|
-
r#"digraph {{
|
|
151
|
-
rankdir=TB;
|
|
152
|
-
|
|
153
|
-
"Name:BasicObject" [label="BasicObject",shape=hexagon];
|
|
154
|
-
"Name:BasicObject" -> "def_{basic_object_def}" [dir=both];
|
|
155
|
-
"Name:Class" [label="Class",shape=hexagon];
|
|
156
|
-
"Name:Class" -> "def_{class_def}" [dir=both];
|
|
157
|
-
"Name:Kernel" [label="Kernel",shape=hexagon];
|
|
158
|
-
"Name:Kernel" -> "def_{kernel_def}" [dir=both];
|
|
159
|
-
"Name:Module" [label="Module",shape=hexagon];
|
|
160
|
-
"Name:Module" -> "def_{module_def}" [dir=both];
|
|
161
|
-
"Name:Object" [label="Object",shape=hexagon];
|
|
162
|
-
"Name:Object" -> "def_{object_def}" [dir=both];
|
|
163
|
-
"Name:TestClass" [label="TestClass",shape=hexagon];
|
|
164
|
-
"Name:TestClass" -> "def_{test_class_def}" [dir=both];
|
|
165
|
-
"Name:TestModule" [label="TestModule",shape=hexagon];
|
|
166
|
-
"Name:TestModule" -> "def_{test_module_def}" [dir=both];
|
|
167
|
-
|
|
168
|
-
"def_{basic_object_def}" [label="Class(BasicObject)",shape=ellipse];
|
|
169
|
-
"def_{class_def}" [label="Class(Class)",shape=ellipse];
|
|
170
|
-
"def_{module_def}" [label="Class(Module)",shape=ellipse];
|
|
171
|
-
"def_{object_def}" [label="Class(Object)",shape=ellipse];
|
|
172
|
-
"def_{test_class_def}" [label="Class(TestClass)",shape=ellipse];
|
|
173
|
-
"def_{kernel_def}" [label="Module(Kernel)",shape=ellipse];
|
|
174
|
-
"def_{test_module_def}" [label="Module(TestModule)",shape=ellipse];
|
|
175
|
-
|
|
176
|
-
"file:///test.rb" [label="test.rb",shape=box];
|
|
177
|
-
"def_{test_class_def}" -> "file:///test.rb";
|
|
178
|
-
"def_{test_module_def}" -> "file:///test.rb";
|
|
179
|
-
"rubydex:built-in" [label="rubydex:built-in",shape=box];
|
|
180
|
-
"def_{basic_object_def}" -> "rubydex:built-in";
|
|
181
|
-
"def_{kernel_def}" -> "rubydex:built-in";
|
|
182
|
-
"def_{object_def}" -> "rubydex:built-in";
|
|
183
|
-
"def_{module_def}" -> "rubydex:built-in";
|
|
184
|
-
"def_{class_def}" -> "rubydex:built-in";
|
|
185
|
-
|
|
186
|
-
}}
|
|
187
|
-
"#
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
assert_eq!(dot_output, expected);
|
|
191
|
-
}
|
|
192
|
-
}
|