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.
- checksums.yaml +4 -4
- data/README.md +4 -20
- data/ext/rubydex/definition.c +34 -0
- data/ext/rubydex/definition.h +3 -0
- data/ext/rubydex/diagnostic.c +2 -49
- data/ext/rubydex/extconf.rb +4 -2
- data/ext/rubydex/graph.c +59 -11
- data/lib/rubydex/cli/command/lint/explain.rb +8 -6
- data/lib/rubydex/cli/command/lint.rb +10 -4
- data/lib/rubydex/linter/rule_loader.rb +1 -1
- data/lib/rubydex/rules/dynamic_ancestor.rb +29 -0
- data/lib/rubydex/rules/dynamic_constant_reference.rb +25 -0
- data/lib/rubydex/rules/dynamic_singleton_definition.rb +30 -0
- data/lib/rubydex/rules/invalid_constant_visibility.rb +28 -0
- data/lib/rubydex/rules/invalid_method_visibility.rb +28 -0
- data/lib/rubydex/rules/parse_error.rb +25 -0
- data/lib/rubydex/rules/parse_warning.rb +28 -0
- data/lib/rubydex/rules/top_level_mixin_self.rb +27 -0
- data/lib/rubydex/rules/undefined_constant_visibility_target.rb +27 -0
- data/lib/rubydex/rules/undefined_method_visibility_target.rb +27 -0
- data/lib/rubydex/skill.rb +1 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +2 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +19 -4
- data/rbi/rubydex.rbi +32 -130
- data/rust/rubydex/Cargo.toml +4 -0
- data/rust/rubydex/src/bin/generate_ruby_rules.rs +94 -0
- data/rust/rubydex/src/diagnostic.rs +142 -51
- data/rust/rubydex/src/query.rs +191 -1
- data/rust/rubydex-sys/src/definition_api.rs +68 -1
- data/rust/rubydex-sys/src/diagnostic_api.rs +0 -35
- data/rust/rubydex-sys/src/graph_api.rs +112 -11
- data/rust/rubydex-sys/src/name_api.rs +47 -11
- 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
data/lib/rubydex/version.rb
CHANGED
data/lib/rubydex.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
179
|
-
class Rubydex::
|
|
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
|
|
895
|
-
|
|
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/rubydex/Cargo.toml
CHANGED
|
@@ -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
|
-
($($(#[$
|
|
67
|
+
($($(#[doc = $documentation:literal])+ $rule:ident => $severity:ident),+ $(,)?) => {
|
|
68
68
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
69
69
|
pub enum Rule {
|
|
70
|
-
$($(#[$
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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 {
|