rubydex 0.3.0 → 0.4.1

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -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/definition.c +34 -0
  8. data/ext/rubydex/definition.h +3 -0
  9. data/ext/rubydex/diagnostic.c +28 -1
  10. data/ext/rubydex/diagnostic.h +2 -0
  11. data/ext/rubydex/extconf.rb +4 -2
  12. data/ext/rubydex/graph.c +71 -55
  13. data/ext/rubydex/graph.h +6 -0
  14. data/ext/rubydex/query.c +398 -16
  15. data/ext/rubydex/rubydex.c +2 -0
  16. data/ext/rubydex/utils.c +11 -4
  17. data/lib/ruby_lsp/rubydex/addon.rb +211 -0
  18. data/lib/rubydex/cli/command/console.rb +55 -0
  19. data/lib/rubydex/cli/command/lint/explain.rb +76 -0
  20. data/lib/rubydex/cli/command/lint.rb +208 -0
  21. data/lib/rubydex/cli/command/mcp.rb +30 -0
  22. data/lib/rubydex/cli/command/query.rb +70 -0
  23. data/lib/rubydex/cli/command/skill.rb +69 -0
  24. data/lib/rubydex/cli/command.rb +168 -0
  25. data/lib/rubydex/cli.rb +93 -0
  26. data/lib/rubydex/config.rb +59 -0
  27. data/lib/rubydex/diagnostic.rb +12 -3
  28. data/lib/rubydex/errors.rb +42 -1
  29. data/lib/rubydex/graph.rb +10 -3
  30. data/lib/rubydex/linter/custom_rule.rb +97 -0
  31. data/lib/rubydex/linter/helpers/path_helpers.rb +78 -0
  32. data/lib/rubydex/linter/helpers/source_access_helpers.rb +31 -0
  33. data/lib/rubydex/linter/rule_loader.rb +36 -0
  34. data/lib/rubydex/linter/rule_test_case.rb +343 -0
  35. data/lib/rubydex/linter/runner.rb +56 -0
  36. data/lib/rubydex/linter.rb +19 -0
  37. data/lib/rubydex/location.rb +3 -0
  38. data/lib/rubydex/mcp_server.rb +1 -2
  39. data/lib/rubydex/related_information.rb +17 -0
  40. data/lib/rubydex/rule.rb +33 -0
  41. data/lib/rubydex/rules/dynamic_ancestor.rb +29 -0
  42. data/lib/rubydex/rules/dynamic_constant_reference.rb +25 -0
  43. data/lib/rubydex/rules/dynamic_singleton_definition.rb +30 -0
  44. data/lib/rubydex/rules/invalid_constant_visibility.rb +28 -0
  45. data/lib/rubydex/rules/invalid_method_visibility.rb +28 -0
  46. data/lib/rubydex/rules/parse_error.rb +25 -0
  47. data/lib/rubydex/rules/parse_warning.rb +28 -0
  48. data/lib/rubydex/rules/top_level_mixin_self.rb +27 -0
  49. data/lib/rubydex/rules/undefined_constant_visibility_target.rb +27 -0
  50. data/lib/rubydex/rules/undefined_method_visibility_target.rb +27 -0
  51. data/lib/rubydex/severity.rb +70 -0
  52. data/lib/rubydex/skill.rb +89 -0
  53. data/lib/rubydex/skill_registry.rb +62 -0
  54. data/lib/rubydex/version.rb +1 -1
  55. data/lib/rubydex.rb +8 -0
  56. data/lib/rubydex_linter/rules/rule_structure.rb +140 -0
  57. data/rbi/rubydex.rbi +464 -21
  58. data/rust/Cargo.lock +2 -2
  59. data/rust/rubydex/Cargo.toml +5 -1
  60. data/rust/rubydex/benches/graph_memory.rs +3 -5
  61. data/rust/rubydex/src/bin/generate_ruby_rules.rs +94 -0
  62. data/rust/rubydex/src/config.rs +538 -157
  63. data/rust/rubydex/src/diagnostic.rs +162 -45
  64. data/rust/rubydex/src/errors.rs +0 -1
  65. data/rust/rubydex/src/indexing/local_graph.rs +6 -5
  66. data/rust/rubydex/src/indexing/rbs_indexer.rs +270 -6
  67. data/rust/rubydex/src/indexing/ruby_indexer.rs +10 -12
  68. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +134 -81
  69. data/rust/rubydex/src/lib.rs +1 -0
  70. data/rust/rubydex/src/listing.rs +26 -1
  71. data/rust/rubydex/src/main.rs +7 -4
  72. data/rust/rubydex/src/model/declaration.rs +302 -219
  73. data/rust/rubydex/src/model/graph.rs +27 -40
  74. data/rust/rubydex/src/model/name.rs +58 -17
  75. data/rust/rubydex/src/operation/ruby_builder.rs +30 -45
  76. data/rust/rubydex/src/path_helpers.rs +77 -0
  77. data/rust/rubydex/src/query/cypher/schema.rs +63 -0
  78. data/rust/rubydex/src/query/cypher/tests.rs +26 -1
  79. data/rust/rubydex/src/query/cypher.rs +8 -11
  80. data/rust/rubydex/src/query.rs +314 -44
  81. data/rust/rubydex/src/resolution.rs +139 -187
  82. data/rust/rubydex/src/resolution_tests.rs +247 -19
  83. data/rust/rubydex/src/test_utils/context.rs +2 -1
  84. data/rust/rubydex/src/test_utils/graph_test.rs +26 -12
  85. data/rust/rubydex/src/test_utils/local_graph_test.rs +19 -0
  86. data/rust/rubydex/tests/cli.rs +4 -4
  87. data/rust/rubydex-sys/src/config_api.rs +205 -0
  88. data/rust/rubydex-sys/src/cypher_api.rs +791 -0
  89. data/rust/rubydex-sys/src/definition_api.rs +68 -1
  90. data/rust/rubydex-sys/src/diagnostic_api.rs +42 -8
  91. data/rust/rubydex-sys/src/graph_api.rs +125 -205
  92. data/rust/rubydex-sys/src/lib.rs +2 -0
  93. data/rust/rubydex-sys/src/name_api.rs +49 -17
  94. data/rust/rubydex-sys/src/utils.rs +37 -0
  95. data/skills/send-private-method/SKILL.md +133 -0
  96. metadata +42 -2
data/rbi/rubydex.rbi CHANGED
@@ -175,22 +175,114 @@ class Rubydex::AttrAccessorDefinition < Rubydex::Definition; end
175
175
  class Rubydex::AttrReaderDefinition < Rubydex::Definition; end
176
176
  class Rubydex::AttrWriterDefinition < Rubydex::Definition; end
177
177
  class Rubydex::ClassVariableDefinition < Rubydex::Definition; end
178
- class Rubydex::ConstantAliasDefinition < Rubydex::Definition; end
179
- class Rubydex::ConstantDefinition < Rubydex::Definition; end
178
+
179
+ class Rubydex::ConstantAliasDefinition < Rubydex::Definition
180
+ sig { returns(String) }
181
+ def raw_name; end
182
+ end
183
+
184
+ class Rubydex::ConstantDefinition < Rubydex::Definition
185
+ sig { returns(String) }
186
+ def raw_name; end
187
+ end
188
+
180
189
  class Rubydex::GlobalVariableAliasDefinition < Rubydex::Definition; end
181
190
  class Rubydex::GlobalVariableDefinition < Rubydex::Definition; end
182
191
  class Rubydex::InstanceVariableDefinition < Rubydex::Definition; end
183
192
 
184
193
  class Rubydex::MethodAliasDefinition < Rubydex::Definition
194
+ sig { returns(T::Array[Rubydex::Signature]) }
195
+ def signatures; end
196
+
185
197
  sig { returns(T.nilable(Rubydex::Method)) }
186
198
  def target; end
187
199
  end
188
200
 
189
- class Rubydex::MethodDefinition < Rubydex::Definition; end
201
+ class Rubydex::MethodDefinition < Rubydex::Definition
202
+ sig { returns(T::Array[Rubydex::Signature]) }
203
+ def signatures; end
204
+ end
205
+
206
+ class Rubydex::Signature
207
+ sig { params(parameters: T::Array[Rubydex::Signature::Parameter]).void }
208
+ def initialize(parameters); end
209
+
210
+ sig { returns(T::Array[Rubydex::Signature::Parameter]) }
211
+ def parameters; end
212
+
213
+ sig do
214
+ returns([
215
+ T::Array[Rubydex::Signature::PositionalParameter],
216
+ T::Array[Rubydex::Signature::OptionalPositionalParameter],
217
+ T.nilable(Rubydex::Signature::RestPositionalParameter),
218
+ T::Array[Rubydex::Signature::PostParameter],
219
+ T::Array[Rubydex::Signature::KeywordParameter],
220
+ T::Array[Rubydex::Signature::OptionalKeywordParameter],
221
+ T.nilable(Rubydex::Signature::RestKeywordParameter),
222
+ T.nilable(Rubydex::Signature::ForwardParameter),
223
+ T.nilable(Rubydex::Signature::BlockParameter),
224
+ ])
225
+ end
226
+ def deconstruct; end
227
+
228
+ sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) }
229
+ def deconstruct_keys(keys); end
230
+
231
+ sig { returns(T::Array[Rubydex::Signature::PositionalParameter]) }
232
+ def positional_parameters; end
233
+
234
+ sig { returns(T::Array[Rubydex::Signature::OptionalPositionalParameter]) }
235
+ def optional_positional_parameters; end
236
+
237
+ sig { returns(T.nilable(Rubydex::Signature::RestPositionalParameter)) }
238
+ def rest_positional_parameter; end
239
+
240
+ sig { returns(T::Array[Rubydex::Signature::PostParameter]) }
241
+ def post_parameters; end
242
+
243
+ sig { returns(T::Array[Rubydex::Signature::KeywordParameter]) }
244
+ def keyword_parameters; end
245
+
246
+ sig { returns(T::Array[Rubydex::Signature::OptionalKeywordParameter]) }
247
+ def optional_keyword_parameters; end
248
+
249
+ sig { returns(T.nilable(Rubydex::Signature::RestKeywordParameter)) }
250
+ def rest_keyword_parameter; end
251
+
252
+ sig { returns(T.nilable(Rubydex::Signature::ForwardParameter)) }
253
+ def forward_parameter; end
254
+
255
+ sig { returns(T.nilable(Rubydex::Signature::BlockParameter)) }
256
+ def block_parameter; end
257
+ end
258
+
259
+ class Rubydex::Signature::Parameter
260
+ sig { returns(Symbol) }
261
+ def name; end
262
+
263
+ sig { returns(Rubydex::Location) }
264
+ def location; end
265
+
266
+ sig { params(name: Symbol, location: Rubydex::Location).void }
267
+ def initialize(name, location); end
268
+ end
269
+
270
+ class Rubydex::Signature::PositionalParameter < Rubydex::Signature::Parameter; end
271
+ class Rubydex::Signature::OptionalPositionalParameter < Rubydex::Signature::Parameter; end
272
+ class Rubydex::Signature::RestPositionalParameter < Rubydex::Signature::Parameter; end
273
+ class Rubydex::Signature::PostParameter < Rubydex::Signature::Parameter; end
274
+ class Rubydex::Signature::KeywordParameter < Rubydex::Signature::Parameter; end
275
+ class Rubydex::Signature::OptionalKeywordParameter < Rubydex::Signature::Parameter; end
276
+ class Rubydex::Signature::RestKeywordParameter < Rubydex::Signature::Parameter; end
277
+ class Rubydex::Signature::ForwardParameter < Rubydex::Signature::Parameter; end
278
+ class Rubydex::Signature::BlockParameter < Rubydex::Signature::Parameter; end
190
279
 
191
280
  class Rubydex::ModuleDefinition < Rubydex::Definition
192
281
  sig { returns(T::Array[Rubydex::Mixin]) }
193
282
  def mixins; end
283
+
284
+ sig { returns(String) }
285
+ def raw_name; end
194
286
  end
195
287
 
196
288
  class Rubydex::SingletonClassDefinition < Rubydex::Definition
@@ -204,6 +296,9 @@ class Rubydex::ClassDefinition < Rubydex::Definition
204
296
 
205
297
  sig { returns(T::Array[Rubydex::Mixin]) }
206
298
  def mixins; end
299
+
300
+ sig { returns(String) }
301
+ def raw_name; end
207
302
  end
208
303
 
209
304
  class Rubydex::Mixin
@@ -220,9 +315,64 @@ class Rubydex::Include < Rubydex::Mixin; end
220
315
  class Rubydex::Prepend < Rubydex::Mixin; end
221
316
  class Rubydex::Extend < Rubydex::Mixin; end
222
317
 
318
+ module Rubydex::Severity
319
+ # Every severity, ordered from the most to the least severe.
320
+ ALL = T.let(T.unsafe(nil), T::Array[T.class_of(Rubydex::Severity::Base)])
321
+
322
+ sig { params(value: Symbol).returns(T.class_of(Rubydex::Severity::Base)) }
323
+ def self.from_value(value); end
324
+ end
325
+
326
+ class Rubydex::Severity::Base
327
+ abstract!
328
+
329
+ sig { returns(Symbol) }
330
+ def self.value; end
331
+ end
332
+
333
+ class Rubydex::Severity::Error < Rubydex::Severity::Base; end
334
+ class Rubydex::Severity::Warning < Rubydex::Severity::Base; end
335
+ class Rubydex::Severity::Information < Rubydex::Severity::Base; end
336
+ class Rubydex::Severity::Hint < Rubydex::Severity::Base; end
337
+
338
+ class Rubydex::Rule
339
+ abstract!
340
+
341
+ class << self
342
+ abstract!
343
+
344
+ sig { returns(String) }
345
+ def rule_name; end
346
+
347
+ sig { abstract.returns(T.class_of(Rubydex::Severity::Base)) }
348
+ def default_severity; end
349
+
350
+ sig { params(config: Rubydex::LinterConfig).returns(T.class_of(Rubydex::Severity::Base)) }
351
+ def severity(config); end
352
+ end
353
+ end
354
+
355
+ class Rubydex::RelatedInformation
356
+ sig { params(message: String, location: Rubydex::Location).void }
357
+ def initialize(message, location); end
358
+
359
+ sig { returns(Rubydex::Location) }
360
+ def location; end
361
+
362
+ sig { returns(String) }
363
+ def message; end
364
+ end
365
+
223
366
  class Rubydex::Diagnostic
224
- sig { params(rule: Symbol, message: String, location: Rubydex::Location).void }
225
- def initialize(rule:, message:, location:); end
367
+ sig do
368
+ params(
369
+ rule: T.class_of(Rubydex::Rule),
370
+ message: String,
371
+ location: Rubydex::Location,
372
+ related_information: T::Array[Rubydex::RelatedInformation],
373
+ ).void
374
+ end
375
+ def initialize(rule:, message:, location:, related_information: []); end
226
376
 
227
377
  sig { returns(Rubydex::Location) }
228
378
  def location; end
@@ -230,8 +380,179 @@ class Rubydex::Diagnostic
230
380
  sig { returns(String) }
231
381
  def message; end
232
382
 
233
- sig { returns(Symbol) }
383
+ sig { returns(T.class_of(Rubydex::Rule)) }
234
384
  def rule; end
385
+
386
+ sig { returns(T::Array[Rubydex::RelatedInformation]) }
387
+ def related_information; end
388
+ end
389
+
390
+ module Rubydex::Linter; end
391
+ module Rubydex::Linter::Helpers; end
392
+ module Rubydex::Linter::Rules; end
393
+
394
+ module Rubydex::Linter::Helpers::PathHelpers
395
+ extend T::Helpers
396
+
397
+ requires_ancestor { Rubydex::Linter::CustomRule }
398
+
399
+ EXCLUDE_FNMATCH_FLAGS = T.let(T.unsafe(nil), Integer)
400
+ TEST_PATHS = T.let(T.unsafe(nil), T::Array[String])
401
+
402
+ sig do
403
+ params(
404
+ path: String,
405
+ patterns: T::Array[String],
406
+ workspace: String,
407
+ flags: Integer,
408
+ ).returns(T::Boolean)
409
+ end
410
+ def self.path_matches_patterns?(path, patterns, workspace:, flags: 0); end
411
+
412
+ sig { params(location: Rubydex::Location, workspace: String).returns(String) }
413
+ def self.display_path(location, workspace:); end
414
+
415
+ sig do
416
+ params(
417
+ definitions: T::Enumerable[Rubydex::Definition],
418
+ excluded_patterns: T::Array[String],
419
+ ).returns(T::Array[Rubydex::Definition])
420
+ end
421
+ def reject_definitions_in_paths(definitions, excluded_patterns); end
422
+
423
+ sig do
424
+ params(
425
+ definitions: T::Enumerable[Rubydex::Definition],
426
+ patterns: T::Array[String],
427
+ ).returns(T::Array[Rubydex::Definition])
428
+ end
429
+ def select_definitions_in_paths(definitions, patterns); end
430
+
431
+ private
432
+
433
+ sig { params(path: String, patterns: T::Array[String]).returns(T::Boolean) }
434
+ def path_matches_patterns?(path, patterns); end
435
+
436
+ sig { params(path: String).returns(T::Boolean) }
437
+ def test_path?(path); end
438
+
439
+ sig { params(definition: Rubydex::Definition).returns(T.nilable(String)) }
440
+ def path_for_definition(definition); end
441
+ end
442
+
443
+ module Rubydex::Linter::Helpers::SourceAccessHelpers
444
+ sig { params(uri: String).returns(Rubydex::Location) }
445
+ def file_location(uri); end
446
+
447
+ sig { params(uri: String).returns(String) }
448
+ def path_for_uri(uri); end
449
+
450
+ private
451
+
452
+ sig { params(location: Rubydex::Location).returns(T.nilable(String)) }
453
+ def source_for_location(location); end
454
+ end
455
+
456
+ # Base class for semantic lint rules, which collect their diagnostics by walking a resolved graph.
457
+ class Rubydex::Linter::CustomRule < Rubydex::Rule
458
+ abstract!
459
+
460
+ sig { params(graph: Rubydex::Graph, config: Rubydex::LinterConfig).void }
461
+ def initialize(graph, config:); end
462
+
463
+ sig { returns(Rubydex::Graph) }
464
+ def graph; end
465
+
466
+ sig { returns(Rubydex::LinterConfig) }
467
+ def config; end
468
+
469
+ sig { returns(T::Array[Rubydex::Diagnostic]) }
470
+ def diagnostics; end
471
+
472
+ sig { params(definition: Rubydex::Definition).returns(Rubydex::Location) }
473
+ def diagnostic_location(definition); end
474
+
475
+ sig { params(base_name: String).returns(T::Enumerable[Rubydex::Class]) }
476
+ def child_classes(base_name); end
477
+
478
+ sig { params(name: String).returns(Rubydex::Namespace) }
479
+ def required_namespace(name); end
480
+
481
+ sig do
482
+ params(
483
+ namespace: Rubydex::Namespace,
484
+ method_name: String,
485
+ ).returns(Rubydex::Method)
486
+ end
487
+ def required_method(namespace, method_name); end
488
+
489
+ sig { returns(String) }
490
+ def rule_name; end
491
+
492
+ sig { abstract.void }
493
+ def lint; end
494
+
495
+ protected
496
+
497
+ sig do
498
+ params(
499
+ message: String,
500
+ location: Rubydex::Location,
501
+ related_information: T::Array[Rubydex::RelatedInformation],
502
+ ).void
503
+ end
504
+ def add_diagnostic(message, location, related_information: []); end
505
+ end
506
+
507
+ class Rubydex::Linter::MissingGraphDependencyError < StandardError
508
+ sig { params(rule_name: String, dependency: String).void }
509
+ def initialize(rule_name, dependency); end
510
+ end
511
+
512
+ class Rubydex::Linter::RuleLoadError < StandardError; end
513
+
514
+ class Rubydex::Linter::RuleLoader
515
+ RULE_GLOB = T.let(T.unsafe(nil), String)
516
+
517
+ sig { params(workspace_path: String).void }
518
+ def self.load(workspace_path); end
519
+ end
520
+
521
+ class Rubydex::Linter::Rules::RuleStructure < Rubydex::Linter::CustomRule
522
+ include Rubydex::Linter::Helpers::SourceAccessHelpers
523
+
524
+ BASE_RULE_NAME = T.let(T.unsafe(nil), String)
525
+ RULE_NAMESPACE = T.let(T.unsafe(nil), String)
526
+ RULE_FILE_PATTERNS = T.let(T.unsafe(nil), T::Array[String])
527
+ TEST_FILE_PATTERNS = T.let(T.unsafe(nil), T::Array[String])
528
+
529
+ class << self
530
+ sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
531
+ def default_severity; end
532
+ end
533
+
534
+ sig { override.void }
535
+ def lint; end
536
+ end
537
+
538
+ class Rubydex::Linter::Runner
539
+ sig do
540
+ params(
541
+ graph: Rubydex::Graph,
542
+ custom_rules: T::Array[T.class_of(Rubydex::Linter::CustomRule)],
543
+ config: Rubydex::LinterConfig,
544
+ ).void
545
+ end
546
+ def initialize(graph, custom_rules:, config:); end
547
+
548
+ sig { returns(Rubydex::Graph) }
549
+ def graph; end
550
+
551
+ sig { returns(T::Array[T.class_of(Rubydex::Linter::CustomRule)]) }
552
+ def custom_rules; end
553
+
554
+ sig { returns(T::Array[Rubydex::Diagnostic]) }
555
+ def run; end
235
556
  end
236
557
 
237
558
  class Rubydex::Keyword
@@ -273,6 +594,74 @@ end
273
594
  class Rubydex::Error < StandardError; end
274
595
  class Rubydex::AliasCycleError < Rubydex::Error; end
275
596
  class Rubydex::ConfigError < Rubydex::Error; end
597
+ class Rubydex::QueryError < Rubydex::Error; end
598
+ class Rubydex::QuerySyntaxError < Rubydex::QueryError; end
599
+ class Rubydex::QueryExecutionError < Rubydex::QueryError; end
600
+ class Rubydex::StaleQueryResultError < Rubydex::QueryError; end
601
+
602
+ # The configuration of a workspace, parsed from its `rubydex.toml`. It carries both the settings that are global to
603
+ # every built-in tool, such as the workspace being analyzed, and the typed settings of each tool's own section (e.g.
604
+ # `[graph]`, `[linter]`).
605
+ class Rubydex::Config
606
+ class << self
607
+ # Loads the configuration file for `workspace_path/rubydex.toml` if it exists or uses the defaults.
608
+ sig { params(workspace_path: String).returns(Rubydex::Config) }
609
+ def load(workspace_path); end
610
+ end
611
+
612
+ # The linter's settings, read from the `[linter]` section.
613
+ sig { returns(Rubydex::LinterConfig) }
614
+ def linter; end
615
+
616
+ # The configured workspace path, which is usually PWD, except for editors that spawn language servers outside of pwd.
617
+ sig { returns(String) }
618
+ def workspace_path; end
619
+ end
620
+
621
+ # The linter's settings, read from the `[linter]` section of the configuration file.
622
+ class Rubydex::LinterConfig
623
+ # The configured rules, keyed by rule name. Only rules the configuration file mentions appear here, so a rule that
624
+ # was never configured is absent rather than present with its defaults.
625
+ sig { returns(T::Hash[String, Rubydex::RuleConfig]) }
626
+ attr_reader :rules
627
+
628
+ sig { params(rules: T::Hash[String, Rubydex::RuleConfig]).void }
629
+ def initialize(rules); end
630
+
631
+ sig { params(rule_class: T.class_of(Rubydex::Rule)).returns(T::Boolean) }
632
+ def rule_enabled?(rule_class); end
633
+
634
+ sig { params(rule_class: T.class_of(Rubydex::Rule)).returns(T::Array[String]) }
635
+ def excludes_for(rule_class); end
636
+
637
+ sig { params(rule_class: T.class_of(Rubydex::Rule)).returns(T.nilable(T.class_of(Rubydex::Severity::Base))) }
638
+ def severity_for(rule_class); end
639
+ end
640
+
641
+ # The settings of a single linter rule, read from a `[linter.rules.RuleName]` table.
642
+ class Rubydex::RuleConfig
643
+ sig { returns(String) }
644
+ attr_reader :name
645
+
646
+ sig { returns(T::Array[String]) }
647
+ attr_reader :exclude_patterns
648
+
649
+ sig { returns(T.nilable(T.class_of(Rubydex::Severity::Base))) }
650
+ attr_reader :severity
651
+
652
+ sig do
653
+ params(
654
+ name: String,
655
+ enabled: T::Boolean,
656
+ exclude_patterns: T::Array[String],
657
+ severity: T.nilable(T.class_of(Rubydex::Severity::Base)),
658
+ ).void
659
+ end
660
+ def initialize(name, enabled, exclude_patterns = [], severity = nil); end
661
+
662
+ sig { returns(T::Boolean) }
663
+ def enabled?; end
664
+ end
276
665
 
277
666
  class Rubydex::Failure
278
667
  sig { params(message: String).void }
@@ -293,13 +682,55 @@ class Rubydex::Query
293
682
  def schema(format = :table); end
294
683
  end
295
684
 
296
- sig { params(graph: Rubydex::Graph, format: T.any(String, Symbol)).returns(String) }
297
- def render(graph, format = :table); end
685
+ sig { params(graph: Rubydex::Graph).returns(Rubydex::Query::Result) }
686
+ def run(graph); end
687
+ end
688
+
689
+ # The columns and rows produced by one run of a query. Enumerable over its rows.
690
+ class Rubydex::Query::Result
691
+ include Enumerable
692
+ extend T::Generic
693
+
694
+ Elem = type_member { { fixed: T::Hash[String, T.untyped] } }
695
+
696
+ class << self
697
+ private
698
+
699
+ # A result only comes from `Query#run`. The C extension undefines `new`, so a call raises.
700
+ sig { returns(T.noreturn) }
701
+ def new; end
702
+ end
703
+
704
+ sig { returns(T::Array[String]) }
705
+ def columns; end
706
+
707
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
708
+ def rows; end
709
+
710
+ sig { override.params(block: T.proc.params(row: T::Hash[String, T.untyped]).void).returns(T.self_type) }
711
+ sig { override.returns(T::Enumerator[T::Hash[String, T.untyped]]) }
712
+ def each(&block); end
713
+
714
+ sig { returns(Integer) }
715
+ def size; end
716
+
717
+ sig { returns(Integer) }
718
+ def length; end
719
+
720
+ sig { returns(T::Boolean) }
721
+ def empty?; end
722
+
723
+ sig { params(format: T.any(String, Symbol)).returns(String) }
724
+ def render(format = :table); end
298
725
  end
299
726
 
300
727
  class Rubydex::Graph
301
- sig { params(workspace_path: T.nilable(String)).void }
302
- def initialize(workspace_path: nil); end
728
+ class << self
729
+ # Creates a new graph with the loaded configuration. For use cases where the graph must be shared between
730
+ # different tools, do not use this. Create and own a `Config` object instead.
731
+ sig { params(workspace_path: String).returns(T.attached_class) }
732
+ def configure_for_workspace(workspace_path); end
733
+ end
303
734
 
304
735
  sig { params(fully_qualified_name: String).returns(T.nilable(Rubydex::Declaration)) }
305
736
  def [](fully_qualified_name); end
@@ -332,12 +763,13 @@ class Rubydex::Graph
332
763
  sig { returns(T::Array[String]) }
333
764
  def index_workspace; end
334
765
 
335
- # Loads configuration, merging its exclusion patterns into the graph's configuration (the workspace path is never
336
- # overridden). With `config_path` (resolved relative to the workspace path), an explicitly named file that does not
337
- # exist raises `Rubydex::ConfigError`. With no argument, the default `.rubydex` is loaded if present and ignored if
338
- # missing. Raises `Rubydex::ConfigError` if a file cannot be read or is malformed.
339
- sig { params(config_path: T.nilable(String)).void }
340
- def load_config(config_path = nil); end
766
+ # Returns the keyword object for the name, or `nil` if it is not a Ruby keyword
767
+ sig { params(name: String).returns(T.nilable(Rubydex::Keyword)) }
768
+ def keyword(name); end
769
+
770
+ # Loads the configuration in the graph.
771
+ sig { params(config: Rubydex::Config).void }
772
+ def load_config(config); end
341
773
 
342
774
  sig { returns(T::Enumerable[Rubydex::MethodReference]) }
343
775
  def method_references; end
@@ -348,8 +780,18 @@ class Rubydex::Graph
348
780
  sig { returns(T.self_type) }
349
781
  def resolve; end
350
782
 
351
- sig { params(name: String, nesting: T::Array[String]).returns(T.nilable(Rubydex::Declaration)) }
352
- def resolve_constant(name, nesting); end
783
+ sig do
784
+ params(
785
+ name: String,
786
+ context: T.any(
787
+ T::Array[String],
788
+ Rubydex::ClassDefinition,
789
+ Rubydex::SingletonClassDefinition,
790
+ Rubydex::ModuleDefinition,
791
+ ),
792
+ ).returns(T.nilable(Rubydex::Declaration))
793
+ end
794
+ def resolve_constant(name, context); end
353
795
 
354
796
  sig { params(require_path: String, load_paths: T::Array[String]).returns(T.nilable(Rubydex::Document)) }
355
797
  def resolve_require_path(require_path, load_paths); end
@@ -360,15 +802,16 @@ class Rubydex::Graph
360
802
  sig { params(queries: String).returns(T::Enumerable[Rubydex::Declaration]) }
361
803
  def fuzzy_search(*queries); end
362
804
 
805
+ sig { returns(T::Enumerable[Rubydex::Declaration]) }
806
+ def dead_code_candidates; end
807
+
363
808
  sig { params(encoding: String).void }
364
809
  def encoding=(encoding); end
365
810
 
811
+ # The root directory of the workspace being analyzed, which comes from the loaded `Rubydex::Config`
366
812
  sig { returns(String) }
367
813
  def workspace_path; end
368
814
 
369
- sig { params(workspace_path: String).returns(String) }
370
- def workspace_path=(workspace_path); end
371
-
372
815
  sig { returns(T::Array[String]) }
373
816
  def workspace_paths; end
374
817
 
data/rust/Cargo.lock CHANGED
@@ -262,9 +262,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
262
262
 
263
263
  [[package]]
264
264
  name = "cypher-parser"
265
- version = "0.2.0"
265
+ version = "0.8.1"
266
266
  source = "registry+https://github.com/rust-lang/crates.io-index"
267
- checksum = "fed0d1e561d51e651bdf70f8439da293fd0f1fe34d8431059061eadaefc7abb1"
267
+ checksum = "6ada60f869dbe5818d0ee40b06ad988627cc0d692d1ed42d692353bf0af8ddb6"
268
268
 
269
269
  [[package]]
270
270
  name = "difflib"
@@ -15,6 +15,10 @@ categories = ["development-tools"]
15
15
  name = "rubydex_cli"
16
16
  path = "src/main.rs"
17
17
 
18
+ [[bin]]
19
+ name = "generate_ruby_rules"
20
+ path = "src/bin/generate_ruby_rules.rs"
21
+
18
22
  [lib]
19
23
  crate-type = ["rlib"]
20
24
 
@@ -27,7 +31,7 @@ jemalloc_dylib = ["jemalloc", "tikv-jemallocator/disable_initial_exec_tls"]
27
31
  test_utils = ["dep:tempfile"]
28
32
 
29
33
  [dependencies]
30
- cypher-parser = "0.2"
34
+ cypher-parser = "0.8.1"
31
35
  ruby-prism = "1.9.0"
32
36
  ruby-rbs = "0.3"
33
37
  url = "2.5.4"
@@ -3,6 +3,7 @@ mod imp {
3
3
  use std::time::Instant;
4
4
 
5
5
  use rubydex::{
6
+ config::Config,
6
7
  indexing::{self, IndexerBackend},
7
8
  listing,
8
9
  model::graph::Graph,
@@ -27,11 +28,8 @@ mod imp {
27
28
  assert!(workspace_path.is_dir(), "the workspace path must be a directory");
28
29
 
29
30
  let mut graph = Graph::new();
30
- graph.set_workspace_path(workspace_path);
31
-
32
- if let Err(error) = graph.load_config(None) {
33
- eprintln!("{error}");
34
- }
31
+ let config = Config::load(&workspace_path).expect("the workspace configuration must be valid");
32
+ graph.load_config(&config);
35
33
 
36
34
  let time = Instant::now();
37
35