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.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +110 -6
  3. data/THIRD_PARTY_LICENSES.html +271 -2
  4. data/exe/rdx +2 -73
  5. data/ext/rubydex/config.c +140 -0
  6. data/ext/rubydex/config.h +16 -0
  7. data/ext/rubydex/declaration.c +1 -1
  8. data/ext/rubydex/definition.c +32 -4
  9. data/ext/rubydex/diagnostic.c +75 -1
  10. data/ext/rubydex/diagnostic.h +2 -0
  11. data/ext/rubydex/graph.c +27 -48
  12. data/ext/rubydex/graph.h +6 -0
  13. data/ext/rubydex/query.c +487 -0
  14. data/ext/rubydex/query.h +8 -0
  15. data/ext/rubydex/reference.c +60 -0
  16. data/ext/rubydex/rubydex.c +4 -0
  17. data/ext/rubydex/utils.c +23 -4
  18. data/ext/rubydex/utils.h +5 -0
  19. data/lib/ruby_lsp/rubydex/addon.rb +211 -0
  20. data/lib/rubydex/cli/command/console.rb +55 -0
  21. data/lib/rubydex/cli/command/lint/explain.rb +74 -0
  22. data/lib/rubydex/cli/command/lint.rb +202 -0
  23. data/lib/rubydex/cli/command/mcp.rb +30 -0
  24. data/lib/rubydex/cli/command/query.rb +70 -0
  25. data/lib/rubydex/cli/command/skill.rb +69 -0
  26. data/lib/rubydex/cli/command.rb +168 -0
  27. data/lib/rubydex/cli.rb +93 -0
  28. data/lib/rubydex/config.rb +59 -0
  29. data/lib/rubydex/diagnostic.rb +12 -3
  30. data/lib/rubydex/errors.rb +42 -1
  31. data/lib/rubydex/graph.rb +10 -3
  32. data/lib/rubydex/linter/custom_rule.rb +97 -0
  33. data/lib/rubydex/linter/helpers/path_helpers.rb +78 -0
  34. data/lib/rubydex/linter/helpers/source_access_helpers.rb +31 -0
  35. data/lib/rubydex/linter/rule_loader.rb +36 -0
  36. data/lib/rubydex/linter/rule_test_case.rb +343 -0
  37. data/lib/rubydex/linter/runner.rb +56 -0
  38. data/lib/rubydex/linter.rb +19 -0
  39. data/lib/rubydex/location.rb +3 -0
  40. data/lib/rubydex/mcp_server.rb +1 -2
  41. data/lib/rubydex/related_information.rb +17 -0
  42. data/lib/rubydex/rule.rb +33 -0
  43. data/lib/rubydex/severity.rb +70 -0
  44. data/lib/rubydex/skill.rb +88 -0
  45. data/lib/rubydex/skill_registry.rb +62 -0
  46. data/lib/rubydex/version.rb +1 -1
  47. data/lib/rubydex.rb +6 -0
  48. data/lib/rubydex_linter/rules/rule_structure.rb +125 -0
  49. data/rbi/rubydex.rbi +578 -15
  50. data/rust/Cargo.lock +7 -0
  51. data/rust/rubydex/Cargo.toml +1 -0
  52. data/rust/rubydex/benches/graph_memory.rs +20 -4
  53. data/rust/rubydex/src/compile_assertions.rs +15 -0
  54. data/rust/rubydex/src/config.rs +538 -157
  55. data/rust/rubydex/src/diagnostic.rs +66 -40
  56. data/rust/rubydex/src/errors.rs +0 -1
  57. data/rust/rubydex/src/indexing/local_graph.rs +6 -5
  58. data/rust/rubydex/src/indexing/rbs_indexer.rs +284 -8
  59. data/rust/rubydex/src/indexing/ruby_indexer.rs +59 -70
  60. data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +195 -86
  61. data/rust/rubydex/src/lib.rs +1 -0
  62. data/rust/rubydex/src/listing.rs +26 -1
  63. data/rust/rubydex/src/main.rs +9 -128
  64. data/rust/rubydex/src/model/declaration.rs +301 -229
  65. data/rust/rubydex/src/model/definitions.rs +27 -26
  66. data/rust/rubydex/src/model/document.rs +43 -7
  67. data/rust/rubydex/src/model/graph.rs +67 -68
  68. data/rust/rubydex/src/model/id.rs +55 -0
  69. data/rust/rubydex/src/model/ids.rs +21 -9
  70. data/rust/rubydex/src/model/name.rs +88 -19
  71. data/rust/rubydex/src/model/references.rs +16 -13
  72. data/rust/rubydex/src/operation/ruby_builder.rs +78 -104
  73. data/rust/rubydex/src/path_helpers.rs +77 -0
  74. data/rust/rubydex/src/query/cypher/schema.rs +853 -0
  75. data/rust/rubydex/src/query/cypher/schema_info.rs +161 -0
  76. data/rust/rubydex/src/query/cypher/tests.rs +253 -0
  77. data/rust/rubydex/src/query/cypher.rs +54 -0
  78. data/rust/rubydex/src/query.rs +125 -43
  79. data/rust/rubydex/src/resolution.rs +368 -395
  80. data/rust/rubydex/src/resolution_tests.rs +504 -78
  81. data/rust/rubydex/src/test_utils/context.rs +2 -1
  82. data/rust/rubydex/src/test_utils/graph_test.rs +26 -12
  83. data/rust/rubydex/src/test_utils/local_graph_test.rs +19 -0
  84. data/rust/rubydex/tests/cli.rs +4 -4
  85. data/rust/rubydex-sys/src/config_api.rs +205 -0
  86. data/rust/rubydex-sys/src/cypher_api.rs +791 -0
  87. data/rust/rubydex-sys/src/declaration_api.rs +6 -3
  88. data/rust/rubydex-sys/src/definition_api.rs +27 -7
  89. data/rust/rubydex-sys/src/diagnostic_api.rs +77 -8
  90. data/rust/rubydex-sys/src/graph_api.rs +31 -68
  91. data/rust/rubydex-sys/src/lib.rs +2 -0
  92. data/rust/rubydex-sys/src/name_api.rs +2 -6
  93. data/rust/rubydex-sys/src/reference_api.rs +58 -12
  94. data/rust/rubydex-sys/src/utils.rs +37 -0
  95. data/skills/send-private-method/SKILL.md +133 -0
  96. metadata +37 -2
@@ -6,6 +6,7 @@ use crate::{
6
6
  assert_def_comments_eq, assert_def_mixins_eq, assert_def_name_eq, assert_def_name_offset_eq, assert_def_str_eq,
7
7
  assert_def_superclass_ref_eq, assert_definition_at, assert_dependents, assert_local_diagnostics_eq,
8
8
  assert_method_has_receiver, assert_name_path_eq, assert_no_local_diagnostics, assert_string_eq,
9
+ diagnostic::Severity,
9
10
  model::{
10
11
  definitions::{Definition, Parameter, Receiver, Signatures},
11
12
  ids::{StringId, UriId},
@@ -967,7 +968,8 @@ mod class_and_module_tests {
967
968
 
968
969
  assert_local_diagnostics_eq!(
969
970
  &context,
970
- ["dynamic-constant-reference: Dynamic constant reference (1:7-1:10)"]
971
+ ["DynamicConstantReference: Dynamic constant reference (1:7-1:10)"],
972
+ severity: Severity::Information
971
973
  );
972
974
  assert!(context.graph().definitions().is_empty());
973
975
  }
@@ -1071,7 +1073,7 @@ mod class_and_module_tests {
1071
1073
 
1072
1074
  assert_local_diagnostics_eq!(
1073
1075
  &context,
1074
- ["dynamic-constant-reference: Dynamic constant reference (1:8-1:11)"]
1076
+ ["DynamicConstantReference: Dynamic constant reference (1:8-1:11)"]
1075
1077
  );
1076
1078
  assert!(context.graph().definitions().is_empty());
1077
1079
  }
@@ -1173,7 +1175,7 @@ mod method_tests {
1173
1175
 
1174
1176
  assert_local_diagnostics_eq!(
1175
1177
  &context,
1176
- ["dynamic-singleton-definition: Dynamic receiver for singleton method definition (1:1-1:17)"]
1178
+ ["DynamicSingletonDefinition: Dynamic receiver for singleton method definition (1:1-1:17)"]
1177
1179
  );
1178
1180
  assert_eq!(context.graph().definitions().len(), 0);
1179
1181
  assert_method_references_eq!(&context, ["foo"]);
@@ -1416,7 +1418,7 @@ mod singleton_class_tests {
1416
1418
 
1417
1419
  assert_local_diagnostics_eq!(
1418
1420
  &context,
1419
- ["dynamic-singleton-definition: Dynamic singleton class definition (1:1-3:4)"]
1421
+ ["DynamicSingletonDefinition: Dynamic singleton class definition (1:1-3:4)"]
1420
1422
  );
1421
1423
  assert_eq!(context.graph().definitions().len(), 0);
1422
1424
  }
@@ -1557,6 +1559,34 @@ mod visibility_tests {
1557
1559
  });
1558
1560
  }
1559
1561
 
1562
+ #[test]
1563
+ fn index_module_function_with_body() {
1564
+ let context = index_source({
1565
+ "
1566
+ module Foo
1567
+ module_function
1568
+
1569
+ def bar
1570
+ @value = CONST
1571
+ baz(1)
1572
+ end
1573
+ end
1574
+ "
1575
+ });
1576
+
1577
+ assert_no_local_diagnostics!(&context);
1578
+ assert_eq!(context.graph().definitions().len(), 4);
1579
+ assert_eq!(context.graph().document().definitions().len(), 4);
1580
+ assert_eq!(context.graph().document().constant_references().len(), 1);
1581
+ assert_eq!(context.graph().document().method_references().len(), 1);
1582
+ assert_constant_references_eq!(&context, ["CONST"]);
1583
+ assert_method_references_eq!(&context, ["baz"]);
1584
+
1585
+ let methods = context.all_definitions_at("4:3-7:6");
1586
+ assert_eq!(methods.len(), 2);
1587
+ assert_definition_at!(&context, "5:5-5:11", InstanceVariable);
1588
+ }
1589
+
1560
1590
  #[test]
1561
1591
  fn index_def_node_with_visibility_nested() {
1562
1592
  let context = index_source({
@@ -1750,20 +1780,20 @@ mod visibility_tests {
1750
1780
  fn index_private_constant_calls_diagnostics() {
1751
1781
  let context = index_source({
1752
1782
  "
1753
- private_constant :NOT_INDEXED
1754
- self.private_constant :NOT_INDEXED
1755
- foo.private_constant :NOT_INDEXED # not indexed, dynamic receiver
1783
+ private_constant :BAR # not indexed, called at top level
1784
+ self.private_constant :BAR # not indexed, self receiver at top level
1785
+ foo.private_constant :BAR # not indexed, dynamic receiver
1756
1786
 
1757
1787
  module Foo
1758
- private_constant NOT_INDEXED, not_indexed # not indexed, not a symbol
1788
+ private_constant SomeConst, some_method # not indexed, non-literal arguments
1759
1789
  private_constant # not indexed, no arguments
1760
1790
 
1761
1791
  def self.qux
1762
- private_constant :Bar # not indexed, dynamic
1792
+ private_constant :BAR # not indexed, inside a method
1763
1793
  end
1764
1794
 
1765
1795
  def foo
1766
- private_constant :Bar # not indexed, dynamic
1796
+ private_constant :BAR # not indexed, inside a method
1767
1797
  end
1768
1798
  end
1769
1799
  "
@@ -1772,15 +1802,53 @@ mod visibility_tests {
1772
1802
  assert_local_diagnostics_eq!(
1773
1803
  &context,
1774
1804
  vec![
1775
- "invalid-private-constant: Private constant called at top level (1:1-1:30)",
1776
- "invalid-private-constant: Private constant called at top level (2:1-2:35)",
1777
- "invalid-private-constant: Dynamic receiver for private constant (3:1-3:34)",
1778
- "invalid-private-constant: Private constant called with non-symbol argument (6:20-6:31)",
1779
- "invalid-private-constant: Private constant called with non-symbol argument (6:33-6:44)",
1805
+ "InvalidConstantVisibility: `private_constant` called at top level (1:1-1:22)",
1806
+ "InvalidConstantVisibility: `private_constant` called at top level (2:1-2:27)",
1807
+ "InvalidConstantVisibility: Dynamic receiver for `private_constant` (3:1-3:4)",
1808
+ "InvalidConstantVisibility: `private_constant` called with a non-literal argument (6:20-6:29)",
1809
+ "InvalidConstantVisibility: `private_constant` called with a non-literal argument (6:31-6:42)",
1810
+ ],
1811
+ severity: Severity::Warning
1812
+ );
1813
+
1814
+ assert_eq!(context.graph().definitions().len(), 3); // Foo, Foo.qux, Foo#foo
1815
+ }
1816
+
1817
+ #[test]
1818
+ fn index_public_constant_calls_diagnostics() {
1819
+ let context = index_source({
1820
+ "
1821
+ public_constant :BAR # not indexed, called at top level
1822
+ self.public_constant :BAR # not indexed, self receiver at top level
1823
+ foo.public_constant :BAR # not indexed, dynamic receiver
1824
+
1825
+ module Foo
1826
+ public_constant SomeConst, some_method # not indexed, non-literal arguments
1827
+ public_constant # not indexed, no arguments
1828
+
1829
+ def self.qux
1830
+ public_constant :BAR # not indexed, inside a method
1831
+ end
1832
+
1833
+ def foo
1834
+ public_constant :BAR # not indexed, inside a method
1835
+ end
1836
+ end
1837
+ "
1838
+ });
1839
+
1840
+ assert_local_diagnostics_eq!(
1841
+ &context,
1842
+ vec![
1843
+ "InvalidConstantVisibility: `public_constant` called at top level (1:1-1:21)",
1844
+ "InvalidConstantVisibility: `public_constant` called at top level (2:1-2:26)",
1845
+ "InvalidConstantVisibility: Dynamic receiver for `public_constant` (3:1-3:4)",
1846
+ "InvalidConstantVisibility: `public_constant` called with a non-literal argument (6:19-6:28)",
1847
+ "InvalidConstantVisibility: `public_constant` called with a non-literal argument (6:30-6:41)",
1780
1848
  ]
1781
1849
  );
1782
1850
 
1783
- assert_eq!(context.graph().definitions().len(), 3); // Foo, Foo::Qux, Foo#foo
1851
+ assert_eq!(context.graph().definitions().len(), 3); // Foo, Foo.qux, Foo#foo
1784
1852
  }
1785
1853
 
1786
1854
  #[test]
@@ -1853,7 +1921,7 @@ mod visibility_tests {
1853
1921
 
1854
1922
  assert_local_diagnostics_eq!(
1855
1923
  &context,
1856
- vec!["invalid-method-visibility: `private` called with a non-literal argument (4:17-4:27)"]
1924
+ vec!["InvalidMethodVisibility: `private` called with a non-literal argument (4:17-4:27)"]
1857
1925
  );
1858
1926
 
1859
1927
  // :foo is a literal arg, so visibility is still applied
@@ -1879,7 +1947,7 @@ mod visibility_tests {
1879
1947
 
1880
1948
  assert_local_diagnostics_eq!(
1881
1949
  &context,
1882
- vec!["invalid-method-visibility: `private` cannot be called with an explicit receiver (4:3-4:19)"]
1950
+ vec!["InvalidMethodVisibility: `private` cannot be called with an explicit receiver (4:3-4:19)"]
1883
1951
  );
1884
1952
 
1885
1953
  for def in context.graph().definitions().values() {
@@ -1904,7 +1972,7 @@ mod visibility_tests {
1904
1972
 
1905
1973
  assert_local_diagnostics_eq!(
1906
1974
  &context,
1907
- vec!["invalid-method-visibility: `private` called with a non-literal argument (4:11-4:21)"]
1975
+ vec!["InvalidMethodVisibility: `private` called with a non-literal argument (4:11-4:21)"]
1908
1976
  );
1909
1977
 
1910
1978
  // No MethodVisibilityDefinition created
@@ -1928,7 +1996,7 @@ mod visibility_tests {
1928
1996
 
1929
1997
  assert_local_diagnostics_eq!(
1930
1998
  &context,
1931
- vec!["invalid-method-visibility: `private` called with a non-literal argument (2:11-2:23)"]
1999
+ vec!["InvalidMethodVisibility: `private` called with a non-literal argument (2:11-2:23)"]
1932
2000
  );
1933
2001
 
1934
2002
  // No MethodVisibilityDefinition created
@@ -1952,7 +2020,7 @@ mod visibility_tests {
1952
2020
 
1953
2021
  assert_local_diagnostics_eq!(
1954
2022
  &context,
1955
- vec!["invalid-method-visibility: `private` called with a non-literal argument (2:11-2:35)"]
2023
+ vec!["InvalidMethodVisibility: `private` called with a non-literal argument (2:11-2:35)"]
1956
2024
  );
1957
2025
 
1958
2026
  // No MethodVisibilityDefinition or AttrReader created from that call
@@ -1995,9 +2063,7 @@ mod visibility_tests {
1995
2063
  // attr_reader(:foo) returns an array in multi-arg context, invalid for `private`
1996
2064
  assert_local_diagnostics_eq!(
1997
2065
  &context,
1998
- vec![
1999
- "invalid-method-visibility: `private` with `attr_*` is only supported as a single argument (2:11-2:28)"
2000
- ]
2066
+ vec!["InvalidMethodVisibility: `private` with `attr_*` is only supported as a single argument (2:11-2:28)"]
2001
2067
  );
2002
2068
 
2003
2069
  // foo reader still defined via side effects, but public
@@ -2021,10 +2087,29 @@ mod visibility_tests {
2021
2087
 
2022
2088
  assert_no_local_diagnostics!(&context);
2023
2089
 
2024
- assert_definition_at!(&context, "4:20-4:23", MethodVisibility, |def| {
2025
- assert_def_str_eq!(&context, def, "foo()");
2026
- assert_eq!(def.visibility(), &Visibility::ModuleFunction);
2027
- });
2090
+ let defs = context.all_definitions_at("4:20-4:23");
2091
+ assert_eq!(defs.len(), 2, "expected two MethodVisibility defs");
2092
+
2093
+ let mut instance_side = None;
2094
+ let mut singleton_side = None;
2095
+ for def in defs {
2096
+ let Definition::MethodVisibility(method_vis) = def else {
2097
+ panic!("expected MethodVisibility, got {:?}", def.kind());
2098
+ };
2099
+ if method_vis.flags().is_singleton_method_visibility() {
2100
+ singleton_side = Some(method_vis.as_ref());
2101
+ } else {
2102
+ instance_side = Some(method_vis.as_ref());
2103
+ }
2104
+ }
2105
+
2106
+ let instance_side = instance_side.expect("missing instance-side def");
2107
+ assert_def_str_eq!(&context, instance_side, "foo()");
2108
+ assert_eq!(instance_side.visibility(), &Visibility::ModuleFunction);
2109
+
2110
+ let singleton_side = singleton_side.expect("missing singleton-side def");
2111
+ assert_def_str_eq!(&context, singleton_side, "foo()");
2112
+ assert_eq!(singleton_side.visibility(), &Visibility::ModuleFunction);
2028
2113
  }
2029
2114
 
2030
2115
  #[test]
@@ -2041,7 +2126,7 @@ mod visibility_tests {
2041
2126
 
2042
2127
  assert_local_diagnostics_eq!(
2043
2128
  &context,
2044
- vec!["invalid-method-visibility: `module_function` can only be used in modules (4:3-4:23)"]
2129
+ vec!["InvalidMethodVisibility: `module_function` can only be used in modules (4:3-4:23)"]
2045
2130
  );
2046
2131
 
2047
2132
  for def in context.graph().definitions().values() {
@@ -2113,8 +2198,8 @@ mod visibility_tests {
2113
2198
  assert_local_diagnostics_eq!(
2114
2199
  &context,
2115
2200
  vec![
2116
- "invalid-method-visibility: `private` called with a non-literal argument (2:25-2:32)",
2117
- "invalid-method-visibility: `private` called with a non-literal argument (3:11-3:18)",
2201
+ "InvalidMethodVisibility: `private` called with a non-literal argument (2:25-2:32)",
2202
+ "InvalidMethodVisibility: `private` called with a non-literal argument (3:11-3:18)",
2118
2203
  ]
2119
2204
  );
2120
2205
 
@@ -2144,8 +2229,8 @@ mod visibility_tests {
2144
2229
  assert_local_diagnostics_eq!(
2145
2230
  &context,
2146
2231
  vec![
2147
- "invalid-method-visibility: `private` called with a non-literal argument (2:11-2:18)",
2148
- "invalid-method-visibility: `private` called with a non-literal argument (2:20-2:27)",
2232
+ "InvalidMethodVisibility: `private` called with a non-literal argument (2:11-2:18)",
2233
+ "InvalidMethodVisibility: `private` called with a non-literal argument (2:20-2:27)",
2149
2234
  ]
2150
2235
  );
2151
2236
 
@@ -2165,7 +2250,7 @@ mod visibility_tests {
2165
2250
  // module_function in a class is always invalid regardless of args
2166
2251
  assert_local_diagnostics_eq!(
2167
2252
  &context,
2168
- vec!["invalid-method-visibility: `module_function` can only be used in modules (2:3-2:37)"]
2253
+ vec!["InvalidMethodVisibility: `module_function` can only be used in modules (2:3-2:37)"]
2169
2254
  );
2170
2255
 
2171
2256
  for def in context.graph().definitions().values() {
@@ -2251,12 +2336,13 @@ mod visibility_tests {
2251
2336
  assert_local_diagnostics_eq!(
2252
2337
  &context,
2253
2338
  vec![
2254
- "invalid-method-visibility: `private_class_method` called at top level (1:1-1:34)",
2255
- "invalid-method-visibility: `private_class_method` called at top level (2:1-2:39)",
2256
- "invalid-method-visibility: `private_class_method` called with a non-literal argument (6:24-6:35)",
2257
- "invalid-method-visibility: `private_class_method` does not accept `attr_*` arguments (8:24-8:41)",
2258
- "invalid-method-visibility: `private_class_method` requires a singleton method definition (9:24-9:39)",
2259
- ]
2339
+ "InvalidMethodVisibility: `private_class_method` called at top level (1:1-1:34)",
2340
+ "InvalidMethodVisibility: `private_class_method` called at top level (2:1-2:39)",
2341
+ "InvalidMethodVisibility: `private_class_method` called with a non-literal argument (6:24-6:35)",
2342
+ "InvalidMethodVisibility: `private_class_method` does not accept `attr_*` arguments (8:24-8:41)",
2343
+ "InvalidMethodVisibility: `private_class_method` requires a singleton method definition (9:24-9:39)",
2344
+ ],
2345
+ severity: Severity::Warning
2260
2346
  );
2261
2347
  }
2262
2348
 
@@ -2290,7 +2376,7 @@ mod visibility_tests {
2290
2376
 
2291
2377
  assert_local_diagnostics_eq!(
2292
2378
  &context,
2293
- vec!["invalid-method-visibility: `private_class_method` called with a non-literal argument (5:28-5:40)"]
2379
+ vec!["InvalidMethodVisibility: `private_class_method` called with a non-literal argument (5:28-5:40)"]
2294
2380
  );
2295
2381
 
2296
2382
  assert_definition_at!(&context, "5:25-5:26", MethodVisibility, |def| {
@@ -2311,7 +2397,7 @@ mod visibility_tests {
2311
2397
 
2312
2398
  assert_local_diagnostics_eq!(
2313
2399
  &context,
2314
- vec!["invalid-method-visibility: `private_class_method` called at top level (1:1-1:32)"]
2400
+ vec!["InvalidMethodVisibility: `private_class_method` called at top level (1:1-1:32)"]
2315
2401
  );
2316
2402
  assert_constant_references_eq!(&context, ["NESTED_REF"]);
2317
2403
  }
@@ -2396,7 +2482,7 @@ mod visibility_tests {
2396
2482
  assert_local_diagnostics_eq!(
2397
2483
  &context,
2398
2484
  vec![
2399
- "invalid-method-visibility: `private_class_method` array element must be a Symbol, String, or method definition (5:32-5:42)"
2485
+ "InvalidMethodVisibility: `private_class_method` array element must be a Symbol, String, or method definition (5:32-5:42)"
2400
2486
  ]
2401
2487
  );
2402
2488
 
@@ -2424,9 +2510,7 @@ mod visibility_tests {
2424
2510
 
2425
2511
  assert_local_diagnostics_eq!(
2426
2512
  &context,
2427
- vec![
2428
- "invalid-method-visibility: `private_class_method` requires a singleton method definition (2:25-2:49)"
2429
- ]
2513
+ vec!["InvalidMethodVisibility: `private_class_method` requires a singleton method definition (2:25-2:49)"]
2430
2514
  );
2431
2515
 
2432
2516
  for def in context.graph().definitions().values() {
@@ -2458,7 +2542,7 @@ mod visibility_tests {
2458
2542
  assert_local_diagnostics_eq!(
2459
2543
  &context,
2460
2544
  vec![
2461
- "invalid-method-visibility: `private_class_method` array argument must be the only argument (5:24-5:28)"
2545
+ "InvalidMethodVisibility: `private_class_method` array argument must be the only argument (5:24-5:28)"
2462
2546
  ]
2463
2547
  );
2464
2548
 
@@ -2761,8 +2845,8 @@ mod constant_reference_tests {
2761
2845
  assert_local_diagnostics_eq!(
2762
2846
  &context,
2763
2847
  [
2764
- "dynamic-constant-reference: Dynamic constant reference (3:6-3:14)",
2765
- "parse-warning: assigned but unused variable - foo (5:1-5:4)",
2848
+ "DynamicConstantReference: Dynamic constant reference (3:6-3:14)",
2849
+ "ParseWarning: assigned but unused variable - foo (5:1-5:4)",
2766
2850
  ]
2767
2851
  );
2768
2852
 
@@ -2945,7 +3029,7 @@ mod method_reference_tests {
2945
3029
 
2946
3030
  assert_local_diagnostics_eq!(
2947
3031
  &context,
2948
- ["parse-error: unexpected ... when the parent method is not forwarding (31:5-31:8)"]
3032
+ ["ParseError: unexpected ... when the parent method is not forwarding (31:5-31:8)"]
2949
3033
  );
2950
3034
 
2951
3035
  assert_method_references_eq!(
@@ -3031,18 +3115,18 @@ mod method_reference_tests {
3031
3115
  assert_local_diagnostics_eq!(
3032
3116
  &context,
3033
3117
  [
3034
- "parse-warning: possibly useless use of != in void context (1:1-1:7)",
3035
- "parse-warning: possibly useless use of % in void context (2:1-2:6)",
3036
- "parse-warning: possibly useless use of & in void context (3:1-3:6)",
3037
- "parse-warning: possibly useless use of * in void context (5:1-5:6)",
3038
- "parse-warning: possibly useless use of ** in void context (6:1-6:7)",
3039
- "parse-warning: possibly useless use of + in void context (7:1-7:6)",
3040
- "parse-warning: possibly useless use of - in void context (8:1-8:6)",
3041
- "parse-warning: possibly useless use of / in void context (9:1-9:6)",
3042
- "parse-warning: possibly useless use of == in void context (11:1-11:7)",
3043
- "parse-warning: possibly useless use of ^ in void context (14:1-14:6)",
3044
- "parse-warning: possibly useless use of | in void context (15:1-15:6)",
3045
- "parse-warning: possibly useless use of <=> in void context (17:1-17:8)"
3118
+ "ParseWarning: possibly useless use of != in void context (1:1-1:7)",
3119
+ "ParseWarning: possibly useless use of % in void context (2:1-2:6)",
3120
+ "ParseWarning: possibly useless use of & in void context (3:1-3:6)",
3121
+ "ParseWarning: possibly useless use of * in void context (5:1-5:6)",
3122
+ "ParseWarning: possibly useless use of ** in void context (6:1-6:7)",
3123
+ "ParseWarning: possibly useless use of + in void context (7:1-7:6)",
3124
+ "ParseWarning: possibly useless use of - in void context (8:1-8:6)",
3125
+ "ParseWarning: possibly useless use of / in void context (9:1-9:6)",
3126
+ "ParseWarning: possibly useless use of == in void context (11:1-11:7)",
3127
+ "ParseWarning: possibly useless use of ^ in void context (14:1-14:6)",
3128
+ "ParseWarning: possibly useless use of | in void context (15:1-15:6)",
3129
+ "ParseWarning: possibly useless use of <=> in void context (17:1-17:8)"
3046
3130
  ]
3047
3131
  );
3048
3132
 
@@ -3064,7 +3148,7 @@ mod method_reference_tests {
3064
3148
 
3065
3149
  assert_local_diagnostics_eq!(
3066
3150
  &context,
3067
- ["parse-warning: possibly useless use of < in void context (1:1-1:6)"]
3151
+ ["ParseWarning: possibly useless use of < in void context (1:1-1:6)"]
3068
3152
  );
3069
3153
 
3070
3154
  assert_method_references_eq!(&context, ["x", "<", "<=>", "y"]);
@@ -3080,7 +3164,7 @@ mod method_reference_tests {
3080
3164
 
3081
3165
  assert_local_diagnostics_eq!(
3082
3166
  &context,
3083
- ["parse-warning: possibly useless use of <= in void context (1:1-1:7)"]
3167
+ ["ParseWarning: possibly useless use of <= in void context (1:1-1:7)"]
3084
3168
  );
3085
3169
 
3086
3170
  assert_method_references_eq!(&context, ["x", "<=", "<=>", "y"]);
@@ -3096,7 +3180,7 @@ mod method_reference_tests {
3096
3180
 
3097
3181
  assert_local_diagnostics_eq!(
3098
3182
  &context,
3099
- ["parse-warning: possibly useless use of > in void context (1:1-1:6)"]
3183
+ ["ParseWarning: possibly useless use of > in void context (1:1-1:6)"]
3100
3184
  );
3101
3185
 
3102
3186
  assert_method_references_eq!(&context, ["x", "<=>", ">", "y"]);
@@ -3112,7 +3196,7 @@ mod method_reference_tests {
3112
3196
 
3113
3197
  assert_local_diagnostics_eq!(
3114
3198
  &context,
3115
- ["parse-warning: possibly useless use of >= in void context (1:1-1:7)"]
3199
+ ["ParseWarning: possibly useless use of >= in void context (1:1-1:7)"]
3116
3200
  );
3117
3201
 
3118
3202
  assert_method_references_eq!(&context, ["x", "<=>", ">=", "y"]);
@@ -3616,10 +3700,10 @@ mod superclass_tests {
3616
3700
  assert_local_diagnostics_eq!(
3617
3701
  &context,
3618
3702
  [
3619
- "dynamic-ancestor: Dynamic superclass (1:13-1:24)",
3620
- "dynamic-ancestor: Dynamic superclass (2:13-2:16)",
3621
- "dynamic-constant-reference: Dynamic constant reference (4:13-4:16)",
3622
- "dynamic-ancestor: Dynamic superclass (4:13-4:21)",
3703
+ "DynamicAncestor: Dynamic superclass (1:13-1:24)",
3704
+ "DynamicAncestor: Dynamic superclass (2:13-2:16)",
3705
+ "DynamicConstantReference: Dynamic constant reference (4:13-4:16)",
3706
+ "DynamicAncestor: Dynamic superclass (4:13-4:21)",
3623
3707
  ]
3624
3708
  );
3625
3709
 
@@ -3802,15 +3886,15 @@ mod mixin_tests {
3802
3886
  assert_local_diagnostics_eq!(
3803
3887
  &context,
3804
3888
  [
3805
- "dynamic-constant-reference: Dynamic constant reference (1:9-1:12)",
3806
- "dynamic-ancestor: Dynamic mixin argument (1:9-1:17)",
3807
- "dynamic-constant-reference: Dynamic constant reference (2:9-2:12)",
3808
- "dynamic-ancestor: Dynamic mixin argument (2:9-2:17)",
3809
- "dynamic-constant-reference: Dynamic constant reference (3:8-3:11)",
3810
- "dynamic-ancestor: Dynamic mixin argument (3:8-3:16)",
3811
- "dynamic-ancestor: Dynamic mixin argument (5:9-5:12)",
3812
- "dynamic-ancestor: Dynamic mixin argument (6:9-6:12)",
3813
- "dynamic-ancestor: Dynamic mixin argument (7:8-7:11)"
3889
+ "DynamicConstantReference: Dynamic constant reference (1:9-1:12)",
3890
+ "DynamicAncestor: Dynamic mixin argument (1:9-1:17)",
3891
+ "DynamicConstantReference: Dynamic constant reference (2:9-2:12)",
3892
+ "DynamicAncestor: Dynamic mixin argument (2:9-2:17)",
3893
+ "DynamicConstantReference: Dynamic constant reference (3:8-3:11)",
3894
+ "DynamicAncestor: Dynamic mixin argument (3:8-3:16)",
3895
+ "DynamicAncestor: Dynamic mixin argument (5:9-5:12)",
3896
+ "DynamicAncestor: Dynamic mixin argument (6:9-6:12)",
3897
+ "DynamicAncestor: Dynamic mixin argument (7:8-7:11)"
3814
3898
  ]
3815
3899
  );
3816
3900
  assert!(context.graph().definitions().is_empty());
@@ -3829,9 +3913,9 @@ mod mixin_tests {
3829
3913
  assert_local_diagnostics_eq!(
3830
3914
  &context,
3831
3915
  [
3832
- "top-level-mixin-self: Top level mixin self (1:9-1:13)",
3833
- "top-level-mixin-self: Top level mixin self (2:9-2:13)",
3834
- "top-level-mixin-self: Top level mixin self (3:8-3:12)"
3916
+ "TopLevelMixinSelf: Top level mixin self (1:9-1:13)",
3917
+ "TopLevelMixinSelf: Top level mixin self (2:9-2:13)",
3918
+ "TopLevelMixinSelf: Top level mixin self (3:8-3:12)"
3835
3919
  ]
3836
3920
  );
3837
3921
 
@@ -5112,6 +5196,29 @@ mod dynamic_namespace_tests {
5112
5196
  });
5113
5197
  }
5114
5198
 
5199
+ #[test]
5200
+ fn index_extend_self_in_anonymous_module() {
5201
+ let context = index_source({
5202
+ "
5203
+ Module.new do
5204
+ extend self
5205
+ end
5206
+ "
5207
+ });
5208
+ assert_no_local_diagnostics!(&context);
5209
+
5210
+ assert_definition_at!(&context, "1:1-3:4", Module, |anonymous_module| {
5211
+ let extend = anonymous_module.mixins().first().unwrap();
5212
+ let constant_reference = context
5213
+ .graph()
5214
+ .constant_references()
5215
+ .get(extend.constant_reference_id())
5216
+ .unwrap();
5217
+
5218
+ assert_eq!(anonymous_module.name_id(), constant_reference.name_id());
5219
+ });
5220
+ }
5221
+
5115
5222
  #[test]
5116
5223
  fn index_singleton_method_in_class_new() {
5117
5224
  let context = index_source({
@@ -5205,9 +5312,10 @@ mod diagnostic_tests {
5205
5312
  assert_local_diagnostics_eq!(
5206
5313
  &context,
5207
5314
  [
5208
- "parse-error: expected an `end` to close the `class` statement (1:1-1:6)",
5209
- "parse-error: unexpected end-of-input, assuming it is closing the parent top level context (1:10-2:1)"
5210
- ]
5315
+ "ParseError: expected an `end` to close the `class` statement (1:1-1:6)",
5316
+ "ParseError: unexpected end-of-input, assuming it is closing the parent top level context (1:10-2:1)"
5317
+ ],
5318
+ severity: Severity::Error
5211
5319
  );
5212
5320
 
5213
5321
  // We still index the definition, even though it has errors
@@ -5227,7 +5335,8 @@ mod diagnostic_tests {
5227
5335
 
5228
5336
  assert_local_diagnostics_eq!(
5229
5337
  &context,
5230
- ["parse-warning: assigned but unused variable - foo (1:1-1:4)"]
5338
+ ["ParseWarning: assigned but unused variable - foo (1:1-1:4)"],
5339
+ severity: Severity::Warning
5231
5340
  );
5232
5341
  }
5233
5342
  }
@@ -16,6 +16,7 @@ pub mod listing;
16
16
  pub mod model;
17
17
  pub mod offset;
18
18
  pub mod operation;
19
+ pub(crate) mod path_helpers;
19
20
  pub mod position;
20
21
  pub mod query;
21
22
  pub mod resolution;
@@ -1,6 +1,7 @@
1
1
  use crate::{
2
2
  errors::Errors,
3
3
  job_queue::{Job, JobQueue},
4
+ path_helpers,
4
5
  };
5
6
  use crossbeam_channel::{Sender, unbounded};
6
7
  use glob::Pattern;
@@ -129,7 +130,7 @@ pub fn collect_file_paths<S: BuildHasher>(
129
130
  Arc::new(excluded.iter().filter_map(|entry| Pattern::new(entry).ok()).collect());
130
131
 
131
132
  for path in paths {
132
- let Ok(path) = std::path::absolute(&path) else {
133
+ let Ok(path) = std::path::absolute(&path).map(path_helpers::simplified) else {
133
134
  errors_tx
134
135
  .send(Errors::FileError(format!("Failed to resolve path `{path}`")))
135
136
  .expect("errors receiver dropped before run completion");
@@ -420,6 +421,30 @@ mod tests {
420
421
  assert_eq!(files, [foo.to_str().unwrap().to_string()]);
421
422
  }
422
423
 
424
+ #[cfg(windows)]
425
+ #[test]
426
+ fn collect_files_excludes_a_directory_below_a_verbatim_root() {
427
+ // Exclusions are joined onto the workspace path the configuration resolved, which is spelled plainly. A root
428
+ // given verbatim only matches them once it is spelled that way too.
429
+ let context = Context::new();
430
+ context.touch(&PathBuf::from("excluded").join("skipped.rb"));
431
+
432
+ let mut excluded = HashSet::new();
433
+ excluded.insert(
434
+ context
435
+ .absolute_path_to("excluded")
436
+ .to_string_lossy()
437
+ .replace('\\', "/")
438
+ .into_boxed_str(),
439
+ );
440
+
441
+ let (files, errors) =
442
+ collect_file_paths(vec![format!(r"\\?\{}", context.absolute_path().display())], &excluded);
443
+
444
+ assert!(errors.is_empty());
445
+ assert!(files.is_empty(), "expected the exclusion to apply: {files:?}");
446
+ }
447
+
423
448
  #[test]
424
449
  fn collect_files_excludes_nested_files_matching_globs() {
425
450
  let context = Context::new();