rubydex 0.4.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -20
  3. data/ext/rubydex/definition.c +34 -0
  4. data/ext/rubydex/definition.h +3 -0
  5. data/ext/rubydex/diagnostic.c +2 -49
  6. data/ext/rubydex/extconf.rb +4 -2
  7. data/ext/rubydex/graph.c +59 -11
  8. data/lib/rubydex/cli/command/lint/explain.rb +8 -6
  9. data/lib/rubydex/cli/command/lint.rb +10 -4
  10. data/lib/rubydex/linter/rule_loader.rb +1 -1
  11. data/lib/rubydex/rules/dynamic_ancestor.rb +29 -0
  12. data/lib/rubydex/rules/dynamic_constant_reference.rb +25 -0
  13. data/lib/rubydex/rules/dynamic_singleton_definition.rb +30 -0
  14. data/lib/rubydex/rules/invalid_constant_visibility.rb +28 -0
  15. data/lib/rubydex/rules/invalid_method_visibility.rb +28 -0
  16. data/lib/rubydex/rules/parse_error.rb +25 -0
  17. data/lib/rubydex/rules/parse_warning.rb +28 -0
  18. data/lib/rubydex/rules/top_level_mixin_self.rb +27 -0
  19. data/lib/rubydex/rules/undefined_constant_visibility_target.rb +27 -0
  20. data/lib/rubydex/rules/undefined_method_visibility_target.rb +27 -0
  21. data/lib/rubydex/skill.rb +1 -0
  22. data/lib/rubydex/version.rb +1 -1
  23. data/lib/rubydex.rb +2 -0
  24. data/lib/rubydex_linter/rules/rule_structure.rb +19 -4
  25. data/rbi/rubydex.rbi +32 -130
  26. data/rust/rubydex/Cargo.toml +4 -0
  27. data/rust/rubydex/src/bin/generate_ruby_rules.rs +94 -0
  28. data/rust/rubydex/src/diagnostic.rs +142 -51
  29. data/rust/rubydex/src/query.rs +191 -1
  30. data/rust/rubydex-sys/src/definition_api.rs +68 -1
  31. data/rust/rubydex-sys/src/diagnostic_api.rs +0 -35
  32. data/rust/rubydex-sys/src/graph_api.rs +112 -11
  33. data/rust/rubydex-sys/src/name_api.rs +47 -11
  34. metadata +13 -2
@@ -0,0 +1,27 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ # This file is generated by `generate_ruby_rules.rs`. Do not edit this manually.
5
+
6
+ module Rubydex
7
+ module Rules
8
+ # Undefined method visibility target means the analysis couldn't find the definition for the method the code is
9
+ # attempting to change visibility of. It could be defined through meta-programming or it indeed does not exist.
10
+ #
11
+ # ```ruby
12
+ # class Foo
13
+ # private :bar
14
+ # ^^^ Undefined method visibility target. The method `bar` is not defined.
15
+ # end
16
+ # ```
17
+ class UndefinedMethodVisibilityTarget < Rule
18
+ class << self
19
+ # @override
20
+ #: -> singleton(Severity::Base)
21
+ def default_severity
22
+ Severity::Warning
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/rubydex/skill.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "yaml"
3
4
  require "rubydex/errors"
4
5
 
5
6
  module Rubydex
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubydex
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.1"
5
5
  end
data/lib/rubydex.rb CHANGED
@@ -31,3 +31,5 @@ require "rubydex/graph"
31
31
  require "rubydex/declaration"
32
32
  require "rubydex/signature"
33
33
  require "rubydex/reference"
34
+
35
+ Dir.glob("#{__dir__}/rubydex/rules/**/*.rb").each { |file| require file }
@@ -9,7 +9,8 @@ module Rubydex
9
9
  # - Each rule subclass outside a test directory is in a rule directory.
10
10
  # - Each checked rule subclass is in the `Rubydex::Linter::Rules` namespace.
11
11
  #
12
- # The rule directories are `rubydex_linter/rules/` and `lib/rubydex_linter/rules/`.
12
+ # The rule directories are `rubydex_linter/rules/` and `lib/rubydex_linter/rules/`, including
13
+ # `lib/rubydex_linter/rules/` directories in nested gems.
13
14
  # This rule does not report files in those directories that define no rule subclass.
14
15
  class RuleStructure < CustomRule
15
16
  include Helpers::SourceAccessHelpers
@@ -18,7 +19,7 @@ module Rubydex
18
19
  RULE_NAMESPACE = "Rubydex::Linter::Rules" #: String
19
20
  RULE_FILE_PATTERNS = [
20
21
  "rubydex_linter/rules/**/*.rb",
21
- "lib/rubydex_linter/rules/**/*.rb",
22
+ "**/lib/rubydex_linter/rules/**/*.rb",
22
23
  ].freeze #: Array[String]
23
24
  TEST_FILE_PATTERNS = ["test/**/*", "**/test/**/*"].freeze #: Array[String]
24
25
 
@@ -37,6 +38,8 @@ module Rubydex
37
38
  rule_definitions_by_file = {} #: Hash[String, Hash[Rubydex::Class, Definition]]
38
39
 
39
40
  rules.each do |rule|
41
+ rule_docs = []
42
+
40
43
  rule.definitions.each do |rule_definition|
41
44
  uri = rule_definition.document.uri
42
45
  path = path_for_uri(uri)
@@ -48,6 +51,8 @@ module Rubydex
48
51
  elsif !test_file?(path)
49
52
  report_wrong_rule_directory(rule.name, rule_definition)
50
53
  end
54
+
55
+ rule_docs.concat(rule_definition.comments)
51
56
  end
52
57
 
53
58
  rule_definition = rule.definitions.find do |definition|
@@ -55,9 +60,19 @@ module Rubydex
55
60
  path_in_workspace?(path) && (rule_file?(path) || !test_file?(path))
56
61
  end
57
62
  next unless rule_definition
58
- next if rule.name.start_with?("#{RULE_NAMESPACE}::")
59
63
 
60
- report_wrong_rule_namespace(rule.name, rule_definition)
64
+ rule_name = rule.name
65
+
66
+ if rule_docs.empty?
67
+ add_diagnostic(
68
+ "`#{rule_name}` is missing documentation.",
69
+ diagnostic_location(rule_definition),
70
+ )
71
+ end
72
+
73
+ next if rule_name.start_with?("#{RULE_NAMESPACE}::")
74
+
75
+ report_wrong_rule_namespace(rule_name, rule_definition)
61
76
  end
62
77
 
63
78
  rule_definitions_by_file.each do |uri, rule_definitions|
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
 
@@ -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
 
@@ -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
+ }
@@ -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 {