rubydex 0.4.0-aarch64-linux → 0.4.2-aarch64-linux

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -14
  3. data/THIRD_PARTY_LICENSES.html +13 -24
  4. data/ext/rubydex/definition.c +34 -0
  5. data/ext/rubydex/definition.h +3 -0
  6. data/ext/rubydex/diagnostic.c +2 -49
  7. data/ext/rubydex/extconf.rb +4 -2
  8. data/ext/rubydex/graph.c +59 -11
  9. data/lib/rubydex/3.2/rubydex.so +0 -0
  10. data/lib/rubydex/3.3/rubydex.so +0 -0
  11. data/lib/rubydex/3.4/rubydex.so +0 -0
  12. data/lib/rubydex/4.0/rubydex.so +0 -0
  13. data/lib/rubydex/cli/command/dead_code.rb +59 -0
  14. data/lib/rubydex/cli/command/lint/explain.rb +8 -6
  15. data/lib/rubydex/cli/command/lint.rb +10 -4
  16. data/lib/rubydex/librubydex_sys.so +0 -0
  17. data/lib/rubydex/linter/rule_loader.rb +1 -1
  18. data/lib/rubydex/mcp_server/tools/codebase_stats_tool.rb +1 -1
  19. data/lib/rubydex/mcp_server/tools/find_constant_references_tool.rb +4 -4
  20. data/lib/rubydex/mcp_server/tools/find_dead_code_candidates_tool.rb +33 -0
  21. data/lib/rubydex/mcp_server/tools/get_declaration_tool.rb +2 -2
  22. data/lib/rubydex/mcp_server/tools/get_descendants_tool.rb +4 -4
  23. data/lib/rubydex/mcp_server/tools/get_file_declarations_tool.rb +2 -2
  24. data/lib/rubydex/mcp_server/tools/search_declarations_tool.rb +6 -6
  25. data/lib/rubydex/mcp_server.rb +24 -12
  26. data/lib/rubydex/rules/dynamic_ancestor.rb +29 -0
  27. data/lib/rubydex/rules/dynamic_constant_reference.rb +25 -0
  28. data/lib/rubydex/rules/dynamic_singleton_definition.rb +30 -0
  29. data/lib/rubydex/rules/invalid_constant_visibility.rb +28 -0
  30. data/lib/rubydex/rules/invalid_method_visibility.rb +28 -0
  31. data/lib/rubydex/rules/parse_error.rb +25 -0
  32. data/lib/rubydex/rules/parse_warning.rb +28 -0
  33. data/lib/rubydex/rules/top_level_mixin_self.rb +27 -0
  34. data/lib/rubydex/rules/undefined_constant_visibility_target.rb +27 -0
  35. data/lib/rubydex/rules/undefined_method_visibility_target.rb +27 -0
  36. data/lib/rubydex/skill.rb +1 -0
  37. data/lib/rubydex/version.rb +1 -1
  38. data/lib/rubydex.rb +2 -0
  39. data/lib/rubydex_linter/rules/rule_structure.rb +19 -4
  40. data/rbi/rubydex.rbi +32 -130
  41. data/rust/Cargo.lock +17 -11
  42. data/rust/Cargo.toml +3 -0
  43. data/rust/rubydex/Cargo.toml +6 -2
  44. data/rust/rubydex/src/bin/generate_ruby_rules.rs +94 -0
  45. data/rust/rubydex/src/compile_assertions.rs +2 -0
  46. data/rust/rubydex/src/config.rs +1 -1
  47. data/rust/rubydex/src/diagnostic.rs +142 -51
  48. data/rust/rubydex/src/indexing/rbs_indexer.rs +4 -4
  49. data/rust/rubydex/src/indexing/ruby_indexer.rs +26 -18
  50. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +42 -2
  51. data/rust/rubydex/src/indexing.rs +6 -6
  52. data/rust/rubydex/src/listing.rs +12 -12
  53. data/rust/rubydex/src/model/built_in.rs +1 -1
  54. data/rust/rubydex/src/model/comment.rs +4 -4
  55. data/rust/rubydex/src/model/declaration.rs +25 -17
  56. data/rust/rubydex/src/model/document.rs +11 -11
  57. data/rust/rubydex/src/model/graph.rs +3 -3
  58. data/rust/rubydex/src/offset.rs +1 -13
  59. data/rust/rubydex/src/operation/ruby_builder.rs +29 -19
  60. data/rust/rubydex/src/query.rs +193 -2
  61. data/rust/rubydex/src/test_utils/graph_test.rs +2 -2
  62. data/rust/rubydex/src/test_utils/local_graph_test.rs +13 -6
  63. data/rust/rubydex-sys/Cargo.toml +2 -2
  64. data/rust/rubydex-sys/src/definition_api.rs +69 -2
  65. data/rust/rubydex-sys/src/diagnostic_api.rs +0 -35
  66. data/rust/rubydex-sys/src/graph_api.rs +193 -45
  67. data/rust/rubydex-sys/src/name_api.rs +219 -48
  68. metadata +199 -187
data/rbi/rubydex.rbi CHANGED
@@ -175,8 +175,17 @@ 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
@@ -271,6 +280,9 @@ class Rubydex::Signature::BlockParameter < Rubydex::Signature::Parameter; end
271
280
  class Rubydex::ModuleDefinition < Rubydex::Definition
272
281
  sig { returns(T::Array[Rubydex::Mixin]) }
273
282
  def mixins; end
283
+
284
+ sig { returns(String) }
285
+ def raw_name; end
274
286
  end
275
287
 
276
288
  class Rubydex::SingletonClassDefinition < Rubydex::Definition
@@ -284,6 +296,9 @@ class Rubydex::ClassDefinition < Rubydex::Definition
284
296
 
285
297
  sig { returns(T::Array[Rubydex::Mixin]) }
286
298
  def mixins; end
299
+
300
+ sig { returns(String) }
301
+ def raw_name; end
287
302
  end
288
303
 
289
304
  class Rubydex::Mixin
@@ -337,80 +352,6 @@ class Rubydex::Rule
337
352
  end
338
353
  end
339
354
 
340
- module Rubydex::Rules
341
- ALL = T.let(T.unsafe(nil), T::Array[T.class_of(Rubydex::Rule)])
342
- end
343
-
344
- class Rubydex::Rules::ParseError < Rubydex::Rule
345
- class << self
346
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
347
- def default_severity; end
348
- end
349
- end
350
-
351
- class Rubydex::Rules::ParseWarning < Rubydex::Rule
352
- class << self
353
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
354
- def default_severity; end
355
- end
356
- end
357
-
358
- class Rubydex::Rules::DynamicConstantReference < Rubydex::Rule
359
- class << self
360
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
361
- def default_severity; end
362
- end
363
- end
364
-
365
- class Rubydex::Rules::DynamicSingletonDefinition < Rubydex::Rule
366
- class << self
367
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
368
- def default_severity; end
369
- end
370
- end
371
-
372
- class Rubydex::Rules::DynamicAncestor < Rubydex::Rule
373
- class << self
374
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
375
- def default_severity; end
376
- end
377
- end
378
-
379
- class Rubydex::Rules::TopLevelMixinSelf < Rubydex::Rule
380
- class << self
381
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
382
- def default_severity; end
383
- end
384
- end
385
-
386
- class Rubydex::Rules::InvalidConstantVisibility < Rubydex::Rule
387
- class << self
388
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
389
- def default_severity; end
390
- end
391
- end
392
-
393
- class Rubydex::Rules::InvalidMethodVisibility < Rubydex::Rule
394
- class << self
395
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
396
- def default_severity; end
397
- end
398
- end
399
-
400
- class Rubydex::Rules::UndefinedMethodVisibilityTarget < Rubydex::Rule
401
- class << self
402
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
403
- def default_severity; end
404
- end
405
- end
406
-
407
- class Rubydex::Rules::UndefinedConstantVisibilityTarget < Rubydex::Rule
408
- class << self
409
- sig { override.returns(T.class_of(Rubydex::Severity::Base)) }
410
- def default_severity; end
411
- end
412
- end
413
-
414
355
  class Rubydex::RelatedInformation
415
356
  sig { params(message: String, location: Rubydex::Location).void }
416
357
  def initialize(message, location); end
@@ -594,58 +535,6 @@ class Rubydex::Linter::Rules::RuleStructure < Rubydex::Linter::CustomRule
594
535
  def lint; end
595
536
  end
596
537
 
597
- class Rubydex::Linter::RuleTestCase < Minitest::Test
598
- DEFAULT_FILE = T.let(T.unsafe(nil), String)
599
- ANNOTATION_PATTERN = T.let(T.unsafe(nil), Regexp)
600
-
601
- sig { returns(String) }
602
- def workspace_path; end
603
-
604
- sig { params(name: String).void }
605
- def initialize(name); end
606
-
607
- sig { void }
608
- def teardown; end
609
-
610
- sig { returns(T.class_of(Rubydex::Linter::CustomRule)) }
611
- def rule_class; end
612
-
613
- sig { params(sources: T::Hash[String, String]).void }
614
- def add_shared_source(sources); end
615
-
616
- sig { returns(T::Array[String]) }
617
- def ignored_diagnostic_files; end
618
-
619
- sig { returns(Rubydex::LinterConfig) }
620
- def rule_config; end
621
-
622
- sig do
623
- params(
624
- args: T.any(String, T::Hash[T.any(String, Symbol), String]),
625
- rule_builder: T.nilable(T.proc.params(graph: Rubydex::Graph).returns(Rubydex::Linter::CustomRule)),
626
- ).returns(T::Array[Rubydex::Diagnostic])
627
- end
628
- def assert_diagnostics(*args, &rule_builder); end
629
-
630
- sig do
631
- params(
632
- args: T.any(String, T::Hash[T.any(String, Symbol), String]),
633
- rule_builder: T.nilable(T.proc.params(graph: Rubydex::Graph).returns(Rubydex::Linter::CustomRule)),
634
- ).returns(T::Array[Rubydex::Diagnostic])
635
- end
636
- def assert_no_diagnostics(*args, &rule_builder); end
637
-
638
- sig do
639
- params(
640
- dependency: String,
641
- args: T.any(String, T::Hash[T.any(String, Symbol), String]),
642
- after_excluding: T::Array[String],
643
- rule_builder: T.nilable(T.proc.params(graph: Rubydex::Graph).returns(Rubydex::Linter::CustomRule)),
644
- ).void
645
- end
646
- def assert_handles_missing_required_dependency(dependency, *args, after_excluding: [], &rule_builder); end
647
- end
648
-
649
538
  class Rubydex::Linter::Runner
650
539
  sig do
651
540
  params(
@@ -891,8 +780,18 @@ class Rubydex::Graph
891
780
  sig { returns(T.self_type) }
892
781
  def resolve; end
893
782
 
894
- sig { params(name: String, nesting: T::Array[String]).returns(T.nilable(Rubydex::Declaration)) }
895
- 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
896
795
 
897
796
  sig { params(require_path: String, load_paths: T::Array[String]).returns(T.nilable(Rubydex::Document)) }
898
797
  def resolve_require_path(require_path, load_paths); end
@@ -903,6 +802,9 @@ class Rubydex::Graph
903
802
  sig { params(queries: String).returns(T::Enumerable[Rubydex::Declaration]) }
904
803
  def fuzzy_search(*queries); end
905
804
 
805
+ sig { returns(T::Enumerable[Rubydex::Declaration]) }
806
+ def dead_code_candidates; end
807
+
906
808
  sig { params(encoding: String).void }
907
809
  def encoding=(encoding); end
908
810
 
data/rust/Cargo.lock CHANGED
@@ -99,7 +99,7 @@ dependencies = [
99
99
  "quote",
100
100
  "regex",
101
101
  "rustc-hash",
102
- "shlex",
102
+ "shlex 1.3.0",
103
103
  "syn",
104
104
  ]
105
105
 
@@ -151,7 +151,7 @@ version = "1.2.30"
151
151
  source = "registry+https://github.com/rust-lang/crates.io-index"
152
152
  checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
153
153
  dependencies = [
154
- "shlex",
154
+ "shlex 1.3.0",
155
155
  ]
156
156
 
157
157
  [[package]]
@@ -718,9 +718,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
718
718
 
719
719
  [[package]]
720
720
  name = "ruby-prism"
721
- version = "1.9.0"
721
+ version = "2.0.0-rc.2"
722
722
  source = "registry+https://github.com/rust-lang/crates.io-index"
723
- checksum = "3b302f00359d0b5423a600314935ca5f994484e15da2db5345631d28c6e029d6"
723
+ checksum = "6d34b0f30ca0b7233a454827204ee7dc1a70d8faa3a26eae95f09ada5386757c"
724
724
  dependencies = [
725
725
  "ruby-prism-sys",
726
726
  "serde",
@@ -729,12 +729,12 @@ dependencies = [
729
729
 
730
730
  [[package]]
731
731
  name = "ruby-prism-sys"
732
- version = "1.9.0"
732
+ version = "2.0.0-rc.2"
733
733
  source = "registry+https://github.com/rust-lang/crates.io-index"
734
- checksum = "9f85fd9571455bdca2b3bf0b2f543cd13ac39d5de0026a8eb2deb94ffd264f00"
734
+ checksum = "f0e59b94e20c878bb618a355abd00724ad1eb4514341936c6668bea987437a29"
735
735
  dependencies = [
736
- "bindgen",
737
736
  "cc",
737
+ "shlex 2.0.1",
738
738
  ]
739
739
 
740
740
  [[package]]
@@ -760,7 +760,7 @@ dependencies = [
760
760
 
761
761
  [[package]]
762
762
  name = "rubydex"
763
- version = "0.2.6"
763
+ version = "0.4.2"
764
764
  dependencies = [
765
765
  "assert_cmd",
766
766
  "bitflags",
@@ -789,7 +789,7 @@ dependencies = [
789
789
 
790
790
  [[package]]
791
791
  name = "rubydex-sys"
792
- version = "0.2.6"
792
+ version = "0.4.2"
793
793
  dependencies = [
794
794
  "cbindgen",
795
795
  "libc",
@@ -902,6 +902,12 @@ version = "1.3.0"
902
902
  source = "registry+https://github.com/rust-lang/crates.io-index"
903
903
  checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
904
904
 
905
+ [[package]]
906
+ name = "shlex"
907
+ version = "2.0.1"
908
+ source = "registry+https://github.com/rust-lang/crates.io-index"
909
+ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
910
+
905
911
  [[package]]
906
912
  name = "smallvec"
907
913
  version = "1.15.1"
@@ -1326,9 +1332,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
1326
1332
 
1327
1333
  [[package]]
1328
1334
  name = "xxhash-rust"
1329
- version = "0.8.15"
1335
+ version = "0.8.16"
1330
1336
  source = "registry+https://github.com/rust-lang/crates.io-index"
1331
- checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
1337
+ checksum = "4d93c89cdc2d3a63c3ec48ffe926931bdc069eafa8e4402fe6d8f790c9d1e576"
1332
1338
 
1333
1339
  [[package]]
1334
1340
  name = "yoke"
data/rust/Cargo.toml CHANGED
@@ -6,6 +6,9 @@ members = [
6
6
 
7
7
  resolver = "3"
8
8
 
9
+ [workspace.package]
10
+ version = "0.4.2"
11
+
9
12
  [profile.release]
10
13
  lto = true
11
14
  opt-level = 3
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rubydex"
3
- version = "0.2.6"
3
+ version.workspace = true
4
4
  edition = "2024"
5
5
  rust-version = "1.89.0"
6
6
  license = "MIT"
@@ -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
 
@@ -28,7 +32,7 @@ test_utils = ["dep:tempfile"]
28
32
 
29
33
  [dependencies]
30
34
  cypher-parser = "0.8.1"
31
- ruby-prism = "1.9.0"
35
+ ruby-prism = "2.0.0-rc.2"
32
36
  ruby-rbs = "0.3"
33
37
  url = "2.5.4"
34
38
  xxhash-rust = { version = "0.8.15", features = ["xxh3"] }
@@ -0,0 +1,94 @@
1
+ use rubydex::diagnostic::{Rule, Severity};
2
+ use std::collections::HashSet;
3
+ use std::path::Path;
4
+ use std::{env, fs};
5
+
6
+ // Takes the built-in diagnostic rules and generates Ruby classes for them in `lib/rubydex/rules`.
7
+ fn main() {
8
+ let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("..").join("..");
9
+ let directory = root.join("lib").join("rubydex").join("rules");
10
+ let mut generated_files = HashSet::new();
11
+
12
+ for rule in Rule::all().iter().copied() {
13
+ write_rule(&directory, rule, &mut generated_files);
14
+ }
15
+
16
+ // Remove stale rules
17
+ for entry in fs::read_dir(&directory).unwrap() {
18
+ let entry = entry.unwrap();
19
+ let path = entry.path();
20
+
21
+ if path.is_file() {
22
+ let file_name = path.file_name().unwrap().to_string_lossy();
23
+
24
+ if !generated_files.contains(&file_name.to_string()) {
25
+ fs::remove_file(path).unwrap();
26
+ }
27
+ }
28
+ }
29
+ }
30
+
31
+ fn write_rule(dir: &Path, rule: Rule, generated_files: &mut HashSet<String>) {
32
+ let documentation = rule
33
+ .documentation()
34
+ .iter()
35
+ .map(|line| format!(" #{line}").trim_end().to_owned())
36
+ .collect::<Vec<String>>()
37
+ .join("\n");
38
+
39
+ let contents = format!(
40
+ "# typed: strict
41
+ # frozen_string_literal: true
42
+
43
+ # This file is generated by `generate_ruby_rules.rs`. Do not edit this manually.
44
+
45
+ module Rubydex
46
+ module Rules
47
+ {documentation}
48
+ class {name} < Rule
49
+ class << self
50
+ # @override
51
+ #: -> singleton(Severity::Base)
52
+ def default_severity
53
+ {severity}
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ ",
60
+ name = rule.name(),
61
+ severity = to_ruby_severity(rule.default_severity()),
62
+ );
63
+
64
+ let file_name = format!("{}.rb", snake_case(rule.name()));
65
+ fs::write(dir.join(&file_name), contents).unwrap();
66
+ generated_files.insert(file_name);
67
+ }
68
+
69
+ fn to_ruby_severity(severity: Severity) -> &'static str {
70
+ match severity {
71
+ Severity::Error => "Severity::Error",
72
+ Severity::Warning => "Severity::Warning",
73
+ Severity::Information => "Severity::Information",
74
+ Severity::Hint => "Severity::Hint",
75
+ }
76
+ }
77
+
78
+ fn snake_case(name: &str) -> String {
79
+ let mut result = String::with_capacity(name.len() + 4);
80
+
81
+ for (index, character) in name.char_indices() {
82
+ if character.is_uppercase() {
83
+ if index != 0 {
84
+ result.push('_');
85
+ }
86
+
87
+ result.extend(character.to_lowercase());
88
+ } else {
89
+ result.push(character);
90
+ }
91
+ }
92
+
93
+ result
94
+ }
@@ -3,11 +3,13 @@
3
3
  macro_rules! assert_mem_size {
4
4
  ($struct:ident, $size:expr) => {
5
5
  // Check struct name on this assert! error
6
+ #[cfg(target_pointer_width = "64")]
6
7
  const _: () = assert!(
7
8
  std::mem::size_of::<$struct>() == $size,
8
9
  concat!("Incorrect size for `", stringify!($struct), "`")
9
10
  );
10
11
  // Check actual size on this array error
12
+ #[cfg(target_pointer_width = "64")]
11
13
  const _: [(); $size] = [(); std::mem::size_of::<$struct>()];
12
14
  };
13
15
  }
@@ -433,7 +433,7 @@ mod tests {
433
433
 
434
434
  let other = rules.iter().find(|rule| rule.name() == "Other").unwrap();
435
435
  assert!(!other.enabled());
436
- assert!(other.exclude_patterns().is_empty());
436
+ assert_eq!(other.exclude_patterns(), []);
437
437
  assert_eq!(other.severity(), None);
438
438
  }
439
439
 
@@ -64,10 +64,10 @@ pub enum Severity {
64
64
  }
65
65
 
66
66
  macro_rules! rules {
67
- ($($(#[$attribute:meta])* $rule:ident),+ $(,)?) => {
67
+ ($($(#[doc = $documentation:literal])+ $rule:ident => $severity:ident),+ $(,)?) => {
68
68
  #[derive(Debug, Copy, Clone, PartialEq, Eq)]
69
69
  pub enum Rule {
70
- $($(#[$attribute])* $rule,)+
70
+ $($(#[doc = $documentation])+ $rule,)+
71
71
  }
72
72
 
73
73
  impl Rule {
@@ -75,60 +75,151 @@ macro_rules! rules {
75
75
  pub fn all() -> &'static [Self] {
76
76
  &[$(Self::$rule,)+]
77
77
  }
78
+
79
+ #[must_use]
80
+ pub fn name(&self) -> &'static str {
81
+ match self {
82
+ $(Self::$rule => stringify!($rule),)+
83
+ }
84
+ }
85
+
86
+ #[must_use]
87
+ pub fn default_severity(&self) -> Severity {
88
+ match self {
89
+ $(Self::$rule => Severity::$severity,)+
90
+ }
91
+ }
92
+
93
+ #[must_use]
94
+ pub fn documentation(&self) -> &'static [&'static str] {
95
+ match self {
96
+ $(Self::$rule => &[$($documentation,)+],)+
97
+ }
98
+ }
78
99
  }
79
100
  };
80
101
  }
81
102
 
82
103
  rules! {
83
- // Parsing
84
- ParseError,
85
- ParseWarning,
86
-
87
- // Indexing
88
- DynamicConstantReference,
89
- DynamicSingletonDefinition,
90
- DynamicAncestor,
91
- TopLevelMixinSelf,
92
- InvalidConstantVisibility,
93
- InvalidMethodVisibility,
94
-
95
- // Resolution
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
- }
104
+ // ******** Parsing ******** //
105
+
106
+ /// A parse error represents invalid Ruby syntax and a program that will fail to execute. For example, a missing
107
+ /// `end`, an unterminated string, a missing parenthesis.
108
+ ///
109
+ /// ```ruby
110
+ /// class Foo
111
+ /// ^^^^^ Syntax error. Missing end token
112
+ /// ```
113
+ ParseError => Error,
114
+
115
+ /// Parse warnings represent code that has valid syntax, but may not do what the developer expects. For example,
116
+ /// local variables that are completely unused or usage in a void context (creating a line of code that does
117
+ /// nothing).
118
+ ///
119
+ /// ```ruby
120
+ /// CONST = 1
121
+ /// CONST
122
+ /// ^^^^^ Constant used in void context (the expression does nothing).
123
+ /// puts CONST + 2
124
+ /// ```
125
+ ParseWarning => Warning,
126
+
127
+ // ******** Indexing ******** //
128
+
129
+ /// Dynamic constant references cannot be reasoned about statically because they depend on runtime values. The
130
+ /// program may still be valid, but the quality of the analysis degrades.
131
+ ///
132
+ /// ```ruby
133
+ /// var::Foo
134
+ /// ^^^^^^^^ Dynamic constant reference. This might be correct, but it cannot be understood by the analysis.
135
+ /// ```
136
+ DynamicConstantReference => Information,
137
+
138
+ /// Dynamic singleton targets cannot be reasoned about statically because they depend on runtime values. The program
139
+ /// may still be valid, but the quality of the analysis degrades.
140
+ ///
141
+ /// ```ruby
142
+ /// class << var
143
+ /// ^^^ Dynamic singleton target. This might be correct, but it cannot be understood by the analysis.
144
+ /// end
145
+ ///
146
+ /// def var.bar
147
+ /// ^^^ Dynamic singleton target.
148
+ /// end
149
+ /// ```
150
+ DynamicSingletonDefinition => Information,
151
+
152
+ /// Dynamic ancestor references cannot be reasoned about statically because they depend on runtime values. The
153
+ /// program may still be valid, but the quality of the analysis degrades. In the case of ancestors, since they
154
+ /// influence constant resolution, the degradation may be more impactful than other dynamic references.
155
+ ///
156
+ /// ```ruby
157
+ /// class Foo < var
158
+ /// ^^^ Dynamic ancestor reference. This might be correct, but it cannot be understood by the analysis.
159
+ /// include SomeClass.method_call
160
+ /// ^^^^^^^^^^^^^^^^^^^^^ Dynamic ancestor reference.
161
+ /// end
162
+ /// ```
163
+ DynamicAncestor => Information,
164
+
165
+ /// The top level of a Ruby program is the special <main> object. It is not possible to use `include self` or
166
+ /// `extend self` on that object.
167
+ ///
168
+ /// ```ruby
169
+ /// include self
170
+ /// ^^^^ Cannot include self at the top level
171
+ /// extend self
172
+ /// ^^^^ Cannot extend self at the top level
173
+ /// ```
174
+ TopLevelMixinSelf => Information,
175
+
176
+ /// Constant visibility operations that depend on dynamic values cannot be reasoned about statically because they
177
+ /// depend on runtime values. The program may still be valid, but the quality of the analysis degrades.
178
+ ///
179
+ /// ```ruby
180
+ /// var.private_constant :Foo
181
+ /// ^^^ Invalid constant visibility. This might be correct, but it cannot be understood by the analysis.
182
+ ///
183
+ /// private_constant(var)
184
+ /// ^^^ Invalid constant visibility.
185
+ /// ```
186
+ InvalidConstantVisibility => Warning,
187
+
188
+ /// Method visibility operations that depend on dynamic values cannot be reasoned about statically because they
189
+ /// depend on runtime values. The program may still be valid, but the quality of the analysis degrades.
190
+ ///
191
+ /// ```ruby
192
+ /// var.private :foo
193
+ /// ^^^ Invalid method visibility. This might be correct, but it cannot be understood by the analysis.
194
+ ///
195
+ /// private(var)
196
+ /// ^^^ Invalid method visibility.
197
+ /// ```
198
+ InvalidMethodVisibility => Warning,
199
+
200
+ // ******** Resolution ******** //
201
+
202
+ /// Undefined method visibility target means the analysis couldn't find the definition for the method the code is
203
+ /// attempting to change visibility of. It could be defined through meta-programming or it indeed does not exist.
204
+ ///
205
+ /// ```ruby
206
+ /// class Foo
207
+ /// private :bar
208
+ /// ^^^ Undefined method visibility target. The method `bar` is not defined.
209
+ /// end
210
+ /// ```
211
+ UndefinedMethodVisibilityTarget => Warning,
212
+
213
+ /// Undefined constant visibility target means the analysis couldn't find the definition for the constant the code is
214
+ /// attempting to change visibility of. It could be defined through meta-programming or it indeed does not exist.
215
+ ///
216
+ /// ```ruby
217
+ /// class Foo
218
+ /// private_constant :Bar
219
+ /// ^^^ Undefined constant visibility target. The constant `Bar` is not defined.
220
+ /// end
221
+ /// ```
222
+ UndefinedConstantVisibilityTarget => Warning,
132
223
  }
133
224
 
134
225
  impl std::fmt::Display for Rule {