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
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#include "config.h"
|
|
2
|
+
#include "diagnostic.h"
|
|
3
|
+
#include "rustbindings.h"
|
|
4
|
+
#include "utils.h"
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* RDoc parser workaround for https://github.com/ruby/rdoc/issues/1744:
|
|
8
|
+
* mRubydex = rb_define_module("Rubydex")
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
static VALUE mRubydex;
|
|
12
|
+
// Defined here so that the configuration can build them, but implemented in Ruby (`lib/rubydex/config.rb`), like the
|
|
13
|
+
// other value objects handed back to Ruby.
|
|
14
|
+
static VALUE cLinterConfig;
|
|
15
|
+
static VALUE cRuleConfig;
|
|
16
|
+
static ID id_linter;
|
|
17
|
+
|
|
18
|
+
// Free function for Rubydex::Config: releases the parsed configuration allocated by Rust.
|
|
19
|
+
static void config_free(void *ptr) {
|
|
20
|
+
if (ptr) {
|
|
21
|
+
rdx_config_free(ptr);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Body function for rb_ensure while building a Rubydex::LinterConfig.
|
|
26
|
+
static VALUE config_linter_build(VALUE opaque_rule_array) {
|
|
27
|
+
CLinterRuleArray *rule_array = (CLinterRuleArray *)(uintptr_t)opaque_rule_array;
|
|
28
|
+
VALUE rules = rb_hash_new_capa((long)rule_array->len);
|
|
29
|
+
|
|
30
|
+
for (size_t i = 0; i < rule_array->len; i++) {
|
|
31
|
+
CLinterRule rule = rule_array->items[i];
|
|
32
|
+
VALUE rule_name = rb_str_freeze(rb_utf8_str_new(rule.name, (long)rule.name_length));
|
|
33
|
+
VALUE exclude_patterns = rb_ary_new_capa((long)rule.exclude_patterns_length);
|
|
34
|
+
for (size_t j = 0; j < rule.exclude_patterns_length; j++) {
|
|
35
|
+
CConfigString pattern = rule.exclude_patterns[j];
|
|
36
|
+
rb_ary_push(exclude_patterns, rb_str_freeze(rb_utf8_str_new(pattern.data, (long)pattern.length)));
|
|
37
|
+
}
|
|
38
|
+
rb_obj_freeze(exclude_patterns);
|
|
39
|
+
|
|
40
|
+
VALUE severity = rule.severity == NULL
|
|
41
|
+
? Qnil
|
|
42
|
+
: rdxi_build_diagnostic_severity_value(mRubydex, *rule.severity);
|
|
43
|
+
VALUE argv[] = {rule_name, rule.enabled ? Qtrue : Qfalse, exclude_patterns, severity};
|
|
44
|
+
|
|
45
|
+
rb_hash_aset(rules, rule_name, rb_class_new_instance(4, argv, cRuleConfig));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return rb_class_new_instance(1, &rules, cLinterConfig);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Ensure function for rb_ensure while building a Rubydex::LinterConfig.
|
|
52
|
+
static VALUE config_linter_ensure(VALUE opaque_rule_array) {
|
|
53
|
+
CLinterRuleArray *rule_array = (CLinterRuleArray *)(uintptr_t)opaque_rule_array;
|
|
54
|
+
rdx_config_linter_rules_free(*rule_array);
|
|
55
|
+
|
|
56
|
+
return Qnil;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static VALUE config_linter(VALUE config_obj) {
|
|
60
|
+
CLinterRuleArray rule_array = rdx_config_linter_rules(rdxi_config_from_object(config_obj));
|
|
61
|
+
VALUE opaque_rule_array = (VALUE)(uintptr_t)&rule_array;
|
|
62
|
+
|
|
63
|
+
return rb_ensure(config_linter_build, opaque_rule_array, config_linter_ensure, opaque_rule_array);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const rb_data_type_t config_type = {
|
|
67
|
+
.wrap_struct_name = "Rubydex::Config",
|
|
68
|
+
.function = {
|
|
69
|
+
.dmark = NULL,
|
|
70
|
+
.dfree = config_free,
|
|
71
|
+
.dsize = NULL,
|
|
72
|
+
.dcompact = NULL,
|
|
73
|
+
},
|
|
74
|
+
.parent = NULL,
|
|
75
|
+
.data = NULL,
|
|
76
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/*
|
|
80
|
+
* call-seq:
|
|
81
|
+
* Rubydex::Config.load(workspace_path) -> Rubydex::Config
|
|
82
|
+
*
|
|
83
|
+
* Loads the configuration of the workspace rooted at +workspace_path+, which is where its `rubydex.toml` is expected to
|
|
84
|
+
* be. A workspace without a configuration file gets an empty configuration. Raises Rubydex::ConfigError if the file
|
|
85
|
+
* exists, but cannot be read or is malformed.
|
|
86
|
+
*/
|
|
87
|
+
static VALUE rdxr_config_load(VALUE klass, VALUE workspace_path) {
|
|
88
|
+
Check_Type(workspace_path, T_STRING);
|
|
89
|
+
|
|
90
|
+
struct CConfigResult result = rdx_config_load(StringValueCStr(workspace_path));
|
|
91
|
+
if (result.error != NULL) {
|
|
92
|
+
VALUE message = rb_utf8_str_new_cstr(result.error);
|
|
93
|
+
free_c_string(result.error);
|
|
94
|
+
|
|
95
|
+
VALUE config_error = rb_const_get(mRubydex, rb_intern("ConfigError"));
|
|
96
|
+
rb_exc_raise(rb_exc_new_str(config_error, message));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
VALUE config = TypedData_Wrap_Struct(klass, &config_type, result.config);
|
|
100
|
+
rb_ivar_set(config, id_linter, config_linter(config));
|
|
101
|
+
|
|
102
|
+
return config;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* call-seq:
|
|
107
|
+
* workspace_path -> String
|
|
108
|
+
*
|
|
109
|
+
* Returns the root directory of the workspace this configuration was loaded for.
|
|
110
|
+
*/
|
|
111
|
+
static VALUE rdxr_config_workspace_path(VALUE self) {
|
|
112
|
+
const char *result = rdx_config_workspace_path(rdxi_config_from_object(self));
|
|
113
|
+
// A configuration always has a workspace, so there is no absent case to hand back to Ruby. NULL only means the
|
|
114
|
+
// conversion itself failed, which is why this raises instead of returning nil.
|
|
115
|
+
if (result == NULL) {
|
|
116
|
+
rb_raise(rb_eRuntimeError, "Converting workspace path to Ruby string failed");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return rdxi_owned_c_string_to_ruby(result);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
void rdxi_initialize_config(VALUE moduleRubydex) {
|
|
123
|
+
mRubydex = moduleRubydex;
|
|
124
|
+
|
|
125
|
+
cLinterConfig = rb_define_class_under(mRubydex, "LinterConfig", rb_cObject);
|
|
126
|
+
cRuleConfig = rb_define_class_under(mRubydex, "RuleConfig", rb_cObject);
|
|
127
|
+
id_linter = rb_intern("@linter");
|
|
128
|
+
|
|
129
|
+
VALUE cConfig = rb_define_class_under(mRubydex, "Config", rb_cObject);
|
|
130
|
+
rb_undef_alloc_func(cConfig);
|
|
131
|
+
|
|
132
|
+
// A configuration can only be obtained through `load`; `new` would create an object with no Rust data behind it.
|
|
133
|
+
rb_undef_method(rb_singleton_class(cConfig), "new");
|
|
134
|
+
|
|
135
|
+
rb_define_singleton_method(cConfig, "load", rdxr_config_load, 1);
|
|
136
|
+
rb_define_method(cConfig, "workspace_path", rdxr_config_workspace_path, 0);
|
|
137
|
+
|
|
138
|
+
/* Returns the linter settings read from the `[linter]` section. Its rules are empty when the section is absent. */
|
|
139
|
+
rb_define_attr(cConfig, "linter", true, false);
|
|
140
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#ifndef RUBYDEX_CONFIG_H
|
|
2
|
+
#define RUBYDEX_CONFIG_H
|
|
3
|
+
|
|
4
|
+
#include "ruby.h"
|
|
5
|
+
|
|
6
|
+
extern const rb_data_type_t config_type;
|
|
7
|
+
|
|
8
|
+
static inline void *rdxi_config_from_object(VALUE config_obj) {
|
|
9
|
+
void *config;
|
|
10
|
+
TypedData_Get_Struct(config_obj, void *, &config_type, config);
|
|
11
|
+
return config;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
void rdxi_initialize_config(VALUE mRubydex);
|
|
15
|
+
|
|
16
|
+
#endif // RUBYDEX_CONFIG_H
|
data/ext/rubydex/declaration.c
CHANGED
|
@@ -236,7 +236,7 @@ static VALUE rdxr_declaration_owner(VALUE self) {
|
|
|
236
236
|
const CDeclaration *decl = rdx_declaration_owner(graph, data->id);
|
|
237
237
|
|
|
238
238
|
if (decl == NULL) {
|
|
239
|
-
rb_raise(rb_eRuntimeError, "
|
|
239
|
+
rb_raise(rb_eRuntimeError, "Declaration must exist for a valid id");
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
VALUE decl_class = rdxi_declaration_class_for_kind(decl->kind);
|
data/ext/rubydex/definition.c
CHANGED
|
@@ -18,6 +18,7 @@ static VALUE mRubydex;
|
|
|
18
18
|
static VALUE cInclude;
|
|
19
19
|
static VALUE cPrepend;
|
|
20
20
|
static VALUE cExtend;
|
|
21
|
+
static VALUE cDocument;
|
|
21
22
|
VALUE cComment;
|
|
22
23
|
VALUE cDefinition;
|
|
23
24
|
VALUE cClassDefinition;
|
|
@@ -88,6 +89,9 @@ static VALUE rdxr_definition_location(VALUE self) {
|
|
|
88
89
|
void *graph = rdxi_graph_from_handle(self, &data);
|
|
89
90
|
|
|
90
91
|
Location *loc = rdx_definition_location(graph, data->id);
|
|
92
|
+
if (loc == NULL) {
|
|
93
|
+
rb_raise(rb_eRuntimeError, "Definition must exist for a valid id");
|
|
94
|
+
}
|
|
91
95
|
VALUE location = rdxi_build_location_value(loc);
|
|
92
96
|
rdx_location_free(loc);
|
|
93
97
|
|
|
@@ -105,10 +109,11 @@ static VALUE rdxr_definition_comments(VALUE self) {
|
|
|
105
109
|
void *graph = rdxi_graph_from_handle(self, &data);
|
|
106
110
|
|
|
107
111
|
CommentArray *arr = rdx_definition_comments(graph, data->id);
|
|
108
|
-
if (arr == NULL
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
if (arr == NULL) {
|
|
113
|
+
rb_raise(rb_eRuntimeError, "Definition must exist for a valid id");
|
|
114
|
+
}
|
|
115
|
+
if (arr->len == 0) {
|
|
116
|
+
rdx_definition_comments_free(arr);
|
|
112
117
|
return rb_ary_new();
|
|
113
118
|
}
|
|
114
119
|
|
|
@@ -261,6 +266,27 @@ static VALUE rdxr_definition_lexical_nesting(VALUE self) {
|
|
|
261
266
|
return nesting;
|
|
262
267
|
}
|
|
263
268
|
|
|
269
|
+
/*
|
|
270
|
+
* call-seq:
|
|
271
|
+
* document -> Rubydex::Document
|
|
272
|
+
*
|
|
273
|
+
* Returns the document this definition belongs to.
|
|
274
|
+
*/
|
|
275
|
+
static VALUE rdxr_definition_document(VALUE self) {
|
|
276
|
+
HandleData *data;
|
|
277
|
+
void *graph = rdxi_graph_from_handle(self, &data);
|
|
278
|
+
|
|
279
|
+
const uint64_t *uri_id = rdx_definition_document(graph, data->id);
|
|
280
|
+
if (uri_id == NULL) {
|
|
281
|
+
rb_raise(rb_eRuntimeError, "Definition not found");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
VALUE argv[] = {data->graph_obj, ULL2NUM(*uri_id)};
|
|
285
|
+
free_u64(uri_id);
|
|
286
|
+
|
|
287
|
+
return rb_class_new_instance(2, argv, cDocument);
|
|
288
|
+
}
|
|
289
|
+
|
|
264
290
|
static VALUE rdxi_build_constant_reference(VALUE graph_obj, const CConstantReference *cref) {
|
|
265
291
|
VALUE ref_class = (cref->declaration_id == 0)
|
|
266
292
|
? cUnresolvedConstantReference
|
|
@@ -397,6 +423,7 @@ void rdxi_initialize_definition(VALUE mod) {
|
|
|
397
423
|
cInclude = rb_const_get(mRubydex, rb_intern("Include"));
|
|
398
424
|
cPrepend = rb_const_get(mRubydex, rb_intern("Prepend"));
|
|
399
425
|
cExtend = rb_const_get(mRubydex, rb_intern("Extend"));
|
|
426
|
+
cDocument = rb_const_get(mRubydex, rb_intern("Document"));
|
|
400
427
|
|
|
401
428
|
cComment = rb_define_class_under(mRubydex, "Comment", rb_cObject);
|
|
402
429
|
|
|
@@ -412,6 +439,7 @@ void rdxi_initialize_definition(VALUE mod) {
|
|
|
412
439
|
rb_define_method(cDefinition, "declaration", rdxr_definition_declaration, 0);
|
|
413
440
|
rb_define_method(cDefinition, "lexical_owner", rdxr_definition_lexical_owner, 0);
|
|
414
441
|
rb_define_method(cDefinition, "lexical_nesting", rdxr_definition_lexical_nesting, 0);
|
|
442
|
+
rb_define_method(cDefinition, "document", rdxr_definition_document, 0);
|
|
415
443
|
|
|
416
444
|
cClassDefinition = rb_define_class_under(mRubydex, "ClassDefinition", cDefinition);
|
|
417
445
|
rb_define_method(cClassDefinition, "superclass", rdxr_class_definition_superclass, 0);
|
data/ext/rubydex/diagnostic.c
CHANGED
|
@@ -6,6 +6,80 @@
|
|
|
6
6
|
* mRubydex = rb_define_module("Rubydex")
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
static VALUE mRubydex;
|
|
10
|
+
static VALUE mRules;
|
|
9
11
|
VALUE cDiagnostic;
|
|
12
|
+
static VALUE cRule;
|
|
13
|
+
static ID id_default_severity;
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
VALUE rdxi_build_diagnostic_severity_value(VALUE mRubydex, DiagnosticSeverity severity) {
|
|
16
|
+
VALUE mSeverity = rb_const_get(mRubydex, rb_intern("Severity"));
|
|
17
|
+
|
|
18
|
+
switch (severity) {
|
|
19
|
+
case DiagnosticSeverity_Error:
|
|
20
|
+
return rb_const_get(mSeverity, rb_intern("Error"));
|
|
21
|
+
case DiagnosticSeverity_Warning:
|
|
22
|
+
return rb_const_get(mSeverity, rb_intern("Warning"));
|
|
23
|
+
case DiagnosticSeverity_Information:
|
|
24
|
+
return rb_const_get(mSeverity, rb_intern("Information"));
|
|
25
|
+
case DiagnosticSeverity_Hint:
|
|
26
|
+
return rb_const_get(mSeverity, rb_intern("Hint"));
|
|
27
|
+
default:
|
|
28
|
+
rb_raise(rb_eRuntimeError, "Unknown DiagnosticSeverity: %d", severity);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return Qnil;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* call-seq:
|
|
36
|
+
* default_severity -> Rubydex::Severity::Base
|
|
37
|
+
*
|
|
38
|
+
* Returns the Rubydex::Severity subclass the diagnostics of this rule get unless the configuration overrides it.
|
|
39
|
+
*/
|
|
40
|
+
static VALUE rdxr_generated_rule_default_severity(VALUE self) {
|
|
41
|
+
VALUE severity = rb_attr_get(self, id_default_severity);
|
|
42
|
+
|
|
43
|
+
// Class-level instance variables are not inherited, so a class inheriting from a generated rule reaches this without
|
|
44
|
+
// a severity of its own.
|
|
45
|
+
if (NIL_P(severity)) {
|
|
46
|
+
rb_raise(rb_eRuntimeError, "Rule %s has no default severity", rb_class2name(self));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return severity;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Generates a rule class for every rule the graph can report. Severities are resolved here rather than when they are
|
|
53
|
+
// read, which is why Rubydex::Severity is loaded before this extension (see `lib/rubydex.rb`).
|
|
54
|
+
static void define_generated_rules(void) {
|
|
55
|
+
CRuleArray rule_array = rdx_rules();
|
|
56
|
+
VALUE rules = rb_ary_new_capa((long)rule_array.len);
|
|
57
|
+
|
|
58
|
+
for (size_t i = 0; i < rule_array.len; i++) {
|
|
59
|
+
CRule built_in_rule = rule_array.items[i];
|
|
60
|
+
VALUE name = rb_utf8_str_new(built_in_rule.name, (long)built_in_rule.name_length);
|
|
61
|
+
VALUE rule = rb_define_class_under(mRules, StringValueCStr(name), cRule);
|
|
62
|
+
VALUE severity = rdxi_build_diagnostic_severity_value(mRubydex, built_in_rule.default_severity);
|
|
63
|
+
|
|
64
|
+
rb_ivar_set(rule, id_default_severity, severity);
|
|
65
|
+
rb_define_singleton_method(rule, "default_severity", rdxr_generated_rule_default_severity, 0);
|
|
66
|
+
rb_ary_push(rules, rule);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
rdx_rules_free(rule_array);
|
|
70
|
+
rb_define_const(mRules, "ALL", rb_obj_freeze(rules));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
VALUE rdxi_rule_class_from_name(const char *name, size_t length) {
|
|
74
|
+
return rb_const_get_at(mRules, rb_intern2(name, (long)length));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
void rdxi_initialize_diagnostic(VALUE moduleRubydex) {
|
|
78
|
+
mRubydex = moduleRubydex;
|
|
79
|
+
id_default_severity = rb_intern("@default_severity");
|
|
80
|
+
|
|
81
|
+
cDiagnostic = rb_define_class_under(mRubydex, "Diagnostic", rb_cObject);
|
|
82
|
+
cRule = rb_define_class_under(mRubydex, "Rule", rb_cObject);
|
|
83
|
+
mRules = rb_define_module_under(mRubydex, "Rules");
|
|
84
|
+
define_generated_rules();
|
|
85
|
+
}
|
data/ext/rubydex/diagnostic.h
CHANGED
|
@@ -7,5 +7,7 @@
|
|
|
7
7
|
extern VALUE cDiagnostic;
|
|
8
8
|
|
|
9
9
|
void rdxi_initialize_diagnostic(VALUE mRubydex);
|
|
10
|
+
VALUE rdxi_build_diagnostic_severity_value(VALUE mRubydex, DiagnosticSeverity severity);
|
|
11
|
+
VALUE rdxi_rule_class_from_name(const char *name, size_t length);
|
|
10
12
|
|
|
11
13
|
#endif // RUBYDEX_DIAGNOSTIC_H
|
data/ext/rubydex/graph.c
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "graph.h"
|
|
2
|
+
#include "config.h"
|
|
2
3
|
#include "declaration.h"
|
|
3
4
|
#include "diagnostic.h"
|
|
4
5
|
#include "document.h"
|
|
@@ -63,16 +64,25 @@ const rb_data_type_t graph_type = {
|
|
|
63
64
|
},
|
|
64
65
|
.parent = NULL,
|
|
65
66
|
.data = NULL,
|
|
66
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
67
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
67
68
|
};
|
|
68
69
|
|
|
69
|
-
// Custom allocator for the Graph class. Calls into Rust to create a new
|
|
70
|
-
// as
|
|
70
|
+
// Custom allocator for the Graph class. Calls into Rust to create a new Box<RwLock<Graph>> that gets stored
|
|
71
|
+
// internally as an opaque void pointer. The RwLock provides thread-safe concurrent access across Ractors.
|
|
71
72
|
static VALUE rdxr_graph_alloc(VALUE klass) {
|
|
72
73
|
void *graph = rdx_graph_new();
|
|
73
74
|
return TypedData_Wrap_Struct(klass, &graph_type, graph);
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
// Graph is intentionally non-copyable. A dup/clone of the TypedData wrapper would alias the
|
|
78
|
+
// same underlying Rust allocation (double-free on GC) or, if deep-copied, balloon memory use.
|
|
79
|
+
// Block both paths: `dup` dispatches to `initialize_copy`, `clone` to `initialize_clone`.
|
|
80
|
+
static VALUE rdxr_graph_initialize_copy(VALUE self, VALUE other) {
|
|
81
|
+
(void)self;
|
|
82
|
+
(void)other;
|
|
83
|
+
rb_raise(rb_eRuntimeError, "Rubydex::Graph cannot be duplicated or cloned");
|
|
84
|
+
}
|
|
85
|
+
|
|
76
86
|
/*
|
|
77
87
|
* call-seq:
|
|
78
88
|
* index_all(file_paths) -> Array[String]
|
|
@@ -483,6 +493,7 @@ static VALUE rdxr_graph_set_encoding(VALUE self, VALUE encoding) {
|
|
|
483
493
|
static VALUE rdxr_graph_resolve_constant(VALUE self, VALUE const_name, VALUE nesting) {
|
|
484
494
|
Check_Type(const_name, T_STRING);
|
|
485
495
|
rdxi_check_array_of_strings(nesting);
|
|
496
|
+
const char *const_name_string = StringValueCStr(const_name);
|
|
486
497
|
|
|
487
498
|
// Convert the given file paths into a char** array, so that we can pass to Rust
|
|
488
499
|
size_t length = RARRAY_LEN(nesting);
|
|
@@ -492,7 +503,7 @@ static VALUE rdxr_graph_resolve_constant(VALUE self, VALUE const_name, VALUE nes
|
|
|
492
503
|
TypedData_Get_Struct(self, void *, &graph_type, graph);
|
|
493
504
|
|
|
494
505
|
const CDeclaration *decl =
|
|
495
|
-
rdx_graph_resolve_constant(graph,
|
|
506
|
+
rdx_graph_resolve_constant(graph, const_name_string, (const char **)converted_file_paths, length);
|
|
496
507
|
|
|
497
508
|
rdxi_free_str_array(converted_file_paths, length);
|
|
498
509
|
|
|
@@ -622,7 +633,7 @@ static VALUE rdxr_graph_diagnostics(VALUE self) {
|
|
|
622
633
|
for (size_t i = 0; i < array->len; i++) {
|
|
623
634
|
DiagnosticEntry entry = array->items[i];
|
|
624
635
|
VALUE message = entry.message == NULL ? Qnil : rb_utf8_str_new_cstr(entry.message);
|
|
625
|
-
VALUE rule =
|
|
636
|
+
VALUE rule = rdxi_rule_class_from_name(entry.rule.name, entry.rule.name_length);
|
|
626
637
|
VALUE location = rdxi_build_location_value(entry.location);
|
|
627
638
|
|
|
628
639
|
VALUE kwargs = rb_hash_new();
|
|
@@ -778,6 +789,7 @@ static VALUE rdxr_graph_complete_method_argument(int argc, VALUE *argv, VALUE se
|
|
|
778
789
|
|
|
779
790
|
Check_Type(name, T_STRING);
|
|
780
791
|
rdxi_check_array_of_strings(nesting);
|
|
792
|
+
const char *name_string = StringValueCStr(name);
|
|
781
793
|
|
|
782
794
|
const char *self_receiver = extract_self_receiver(opts);
|
|
783
795
|
|
|
@@ -788,7 +800,7 @@ static VALUE rdxr_graph_complete_method_argument(int argc, VALUE *argv, VALUE se
|
|
|
788
800
|
char **converted_nesting = rdxi_str_array_to_char(nesting, nesting_count);
|
|
789
801
|
|
|
790
802
|
struct CompletionResult result = rdx_graph_complete_method_argument(
|
|
791
|
-
graph,
|
|
803
|
+
graph, name_string, (const char *const *)converted_nesting, nesting_count, self_receiver);
|
|
792
804
|
|
|
793
805
|
rdxi_free_str_array(converted_nesting, nesting_count);
|
|
794
806
|
return completion_result_to_ruby_array(result, self);
|
|
@@ -863,52 +875,18 @@ static VALUE rdxr_graph_workspace_path(VALUE self) {
|
|
|
863
875
|
|
|
864
876
|
/*
|
|
865
877
|
* call-seq:
|
|
866
|
-
*
|
|
878
|
+
* load_config(config) -> void
|
|
867
879
|
*
|
|
868
|
-
*
|
|
880
|
+
* Applies a parsed Rubydex::Config to the graph.
|
|
869
881
|
*/
|
|
870
|
-
static VALUE
|
|
871
|
-
Check_Type(path, T_STRING);
|
|
872
|
-
|
|
873
|
-
void *graph;
|
|
874
|
-
TypedData_Get_Struct(self, void*, &graph_type, graph);
|
|
875
|
-
|
|
876
|
-
rdx_graph_set_workspace_path(graph, StringValueCStr(path));
|
|
877
|
-
return path;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
/*
|
|
881
|
-
* call-seq:
|
|
882
|
-
* load_config(config_path = nil) -> void
|
|
883
|
-
*
|
|
884
|
-
* Loads a configuration file for the graph. If `config_path` is nil, loads the default configuration file at
|
|
885
|
-
* `workspace_path/rubydex.toml` if it exists. Will raise on malformed files or if an explicit path is given but the
|
|
886
|
-
* file does not exist.
|
|
887
|
-
*/
|
|
888
|
-
static VALUE rdxr_graph_load_config(int argc, VALUE *argv, VALUE self) {
|
|
889
|
-
VALUE config_path;
|
|
890
|
-
rb_scan_args(argc, argv, "01", &config_path);
|
|
891
|
-
|
|
882
|
+
static VALUE rdxr_graph_load_config(VALUE self, VALUE config_obj) {
|
|
892
883
|
void *graph;
|
|
893
884
|
TypedData_Get_Struct(self, void *, &graph_type, graph);
|
|
894
885
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
if (!NIL_P(config_path)) {
|
|
898
|
-
Check_Type(config_path, T_STRING);
|
|
899
|
-
config_path_cstr = StringValueCStr(config_path);
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
const char *error = rdx_graph_load_config(graph, config_path_cstr);
|
|
903
|
-
if (error == NULL) {
|
|
904
|
-
return Qnil;
|
|
905
|
-
}
|
|
886
|
+
void *config = rdxi_config_from_object(config_obj);
|
|
887
|
+
rdx_graph_load_config(graph, config);
|
|
906
888
|
|
|
907
|
-
|
|
908
|
-
free_c_string(error);
|
|
909
|
-
|
|
910
|
-
VALUE config_error = rb_const_get(mRubydex, rb_intern("ConfigError"));
|
|
911
|
-
rb_exc_raise(rb_exc_new_str(config_error, message));
|
|
889
|
+
return Qnil;
|
|
912
890
|
}
|
|
913
891
|
|
|
914
892
|
/*
|
|
@@ -943,6 +921,8 @@ void rdxi_initialize_graph(VALUE moduleRubydex) {
|
|
|
943
921
|
id_self_receiver = rb_intern("self_receiver");
|
|
944
922
|
|
|
945
923
|
rb_define_alloc_func(cGraph, rdxr_graph_alloc);
|
|
924
|
+
rb_define_method(cGraph, "initialize_copy", rdxr_graph_initialize_copy, 1);
|
|
925
|
+
rb_define_method(cGraph, "initialize_clone", rdxr_graph_initialize_copy, 1);
|
|
946
926
|
rb_define_method(cGraph, "index_all", rdxr_graph_index_all, 1);
|
|
947
927
|
rb_define_method(cGraph, "index_source", rdxr_graph_index_source, 3);
|
|
948
928
|
rb_define_method(cGraph, "document", rdxr_graph_document, 1);
|
|
@@ -968,7 +948,6 @@ void rdxi_initialize_graph(VALUE moduleRubydex) {
|
|
|
968
948
|
rb_define_method(cGraph, "exclude_patterns", rdxr_graph_exclude_patterns, 1);
|
|
969
949
|
rb_define_method(cGraph, "excluded_patterns", rdxr_graph_excluded_patterns, 0);
|
|
970
950
|
rb_define_method(cGraph, "workspace_path", rdxr_graph_workspace_path, 0);
|
|
971
|
-
rb_define_method(cGraph, "
|
|
972
|
-
rb_define_method(cGraph, "load_config", rdxr_graph_load_config, -1);
|
|
951
|
+
rb_define_method(cGraph, "load_config", rdxr_graph_load_config, 1);
|
|
973
952
|
rb_define_method(cGraph, "keyword", rdxr_graph_keyword, 1);
|
|
974
953
|
}
|
data/ext/rubydex/graph.h
CHANGED
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
extern const rb_data_type_t graph_type;
|
|
7
7
|
|
|
8
|
+
static inline void *rdxi_graph_from_object(VALUE graph_obj) {
|
|
9
|
+
void *graph;
|
|
10
|
+
TypedData_Get_Struct(graph_obj, void *, &graph_type, graph);
|
|
11
|
+
return graph;
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
void rdxi_initialize_graph(VALUE mRubydex);
|
|
9
15
|
|
|
10
16
|
#endif // RUBYDEX_GRAPH_H
|