From 62928d8f569f0803fc181c1683797cc306882602 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Mon, 7 Oct 2024 07:06:11 +0200 Subject: [PATCH 01/16] Implement rule versioning support --- src/ameba/cli/cmd.cr | 9 +++++++++ src/ameba/config.cr | 28 +++++++++++++++++++++++++++ src/ameba/formatter/todo_formatter.cr | 2 ++ src/ameba/runner.cr | 12 ++++++++++-- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/ameba/cli/cmd.cr b/src/ameba/cli/cmd.cr index 6265f5b3b..b4a5c070b 100644 --- a/src/ameba/cli/cmd.cr +++ b/src/ameba/cli/cmd.cr @@ -24,6 +24,9 @@ module Ameba::Cli config.autocorrect = autocorrect config.stdin_filename = stdin_filename + if version = opts.version + config.version = version + end if globs = opts.globs config.globs = globs end @@ -59,6 +62,7 @@ module Ameba::Cli private class Opts property config : Path? + property version : String? property formatter : Symbol | String | Nil property globs : Array(String)? property only : Array(String)? @@ -99,6 +103,11 @@ module Ameba::Cli opts.config = Path[path] unless path.empty? end + parser.on("-u", "--up-to-version VERSION", + "Choose a version") do |version| + opts.version = version + end + parser.on("-f", "--format FORMATTER", "Choose an output formatter: #{Config.formatter_names}") do |formatter| opts.formatter = formatter diff --git a/src/ameba/config.cr b/src/ameba/config.cr index 9a2182746..95fc20253 100644 --- a/src/ameba/config.cr +++ b/src/ameba/config.cr @@ -1,3 +1,4 @@ +require "semantic_version" require "yaml" require "./glob_utils" @@ -63,6 +64,9 @@ class Ameba::Config getter rules : Array(Rule::Base) property severity = Severity::Convention + # Returns an ameba version to be used by `Ameba::Runner`. + property version : SemanticVersion? + # Returns a list of paths (with wildcards) to files. # Represents a list of sources to be inspected. # If globs are not set, it will return default list of files. @@ -105,6 +109,9 @@ class Ameba::Config @excluded = load_array_section(config, "Excluded") @globs = load_array_section(config, "Globs", DEFAULT_GLOBS) + if version = config["version"]?.try(&.as_s).presence + self.version = version + end if formatter_name = load_formatter_name(config) self.formatter = formatter_name end @@ -197,6 +204,16 @@ class Ameba::Config @formatter = formatter.new end + # Sets version from string. + # + # ``` + # config = Ameba::Config.load + # config.version = "1.6.0" + # ``` + def version=(version : String) + @version = SemanticVersion.parse(version) + end + # Updates rule properties. # # ``` @@ -332,6 +349,17 @@ class Ameba::Config @[YAML::Field(key: "Excluded")] property excluded : Array(String)? {% end %} + + {% unless properties["since_version".id] %} + @[YAML::Field(key: "SinceVersion")] + property since_version : String? + {% end %} + + def since_version : SemanticVersion? + if version = @since_version + SemanticVersion.parse(version) + end + end end macro included diff --git a/src/ameba/formatter/todo_formatter.cr b/src/ameba/formatter/todo_formatter.cr index 2744963ad..00dc085c8 100644 --- a/src/ameba/formatter/todo_formatter.cr +++ b/src/ameba/formatter/todo_formatter.cr @@ -62,6 +62,8 @@ module Ameba::Formatter # The point is for the user to remove these configuration records # one by one as the reported problems are removed from the code base. + version: "#{VERSION}" + HEADER end diff --git a/src/ameba/runner.cr b/src/ameba/runner.cr index 03f11b590..b6edd01f5 100644 --- a/src/ameba/runner.cr +++ b/src/ameba/runner.cr @@ -49,6 +49,9 @@ module Ameba # Returns `true` if correctable issues should be autocorrected. private getter? autocorrect : Bool + # Returns an ameba version up to which the rules should be ran. + property version : SemanticVersion? + # Instantiates a runner using a `config`. # # ``` @@ -62,7 +65,12 @@ module Ameba @sources = config.sources @formatter = config.formatter @severity = config.severity - @rules = config.rules.select(&.enabled?).reject!(&.special?) + @version = config.version + @rules = config.rules.select do |rule| + rule.enabled? && !rule.special? && + (!(version = @version) || !(since_version = rule.since_version) || + since_version <= version) + end @autocorrect = config.autocorrect? @unneeded_disable_directive_rule = @@ -70,7 +78,7 @@ module Ameba .find &.class.==(Rule::Lint::UnneededDisableDirective) end - protected def initialize(@rules, @sources, @formatter, @severity, @autocorrect = false) + protected def initialize(@rules, @sources, @formatter, @severity, @autocorrect = false, @version = nil) end # Performs the inspection. Iterates through all sources and test it using From 6a4c31e471df41353f6f6771dc8473d9b2434de3 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Mon, 7 Oct 2024 07:06:53 +0200 Subject: [PATCH 02/16] Version rules from 3 last versions --- src/ameba/rule/documentation/documentation.cr | 1 + src/ameba/rule/documentation/documentation_admonition.cr | 1 + src/ameba/rule/lint/formatting.cr | 1 + src/ameba/rule/lint/literal_assignments_in_expressions.cr | 1 + src/ameba/rule/lint/missing_block_argument.cr | 1 + src/ameba/rule/lint/spec_filename.cr | 1 + src/ameba/rule/lint/typos.cr | 1 + src/ameba/rule/lint/unused_block_argument.cr | 1 + src/ameba/rule/naming/accessor_method_name.cr | 1 + src/ameba/rule/naming/ascii_identifiers.cr | 1 + src/ameba/rule/naming/binary_operator_parameter_name.cr | 1 + src/ameba/rule/naming/block_parameter_name.cr | 1 + src/ameba/rule/naming/filename.cr | 1 + src/ameba/rule/naming/query_bool_methods.cr | 1 + src/ameba/rule/naming/rescued_exceptions_variable_name.cr | 1 + src/ameba/rule/performance/excessive_allocations.cr | 1 + src/ameba/rule/performance/minmax_after_map.cr | 1 + src/ameba/rule/style/parentheses_around_condition.cr | 1 + 18 files changed, 18 insertions(+) diff --git a/src/ameba/rule/documentation/documentation.cr b/src/ameba/rule/documentation/documentation.cr index 45cbfc369..dd1f69456 100644 --- a/src/ameba/rule/documentation/documentation.cr +++ b/src/ameba/rule/documentation/documentation.cr @@ -16,6 +16,7 @@ module Ameba::Rule::Documentation # ``` class Documentation < Base properties do + since_version "1.5.0" enabled false description "Enforces public types to be documented" diff --git a/src/ameba/rule/documentation/documentation_admonition.cr b/src/ameba/rule/documentation/documentation_admonition.cr index 75547750d..5b7993838 100644 --- a/src/ameba/rule/documentation/documentation_admonition.cr +++ b/src/ameba/rule/documentation/documentation_admonition.cr @@ -34,6 +34,7 @@ module Ameba::Rule::Documentation # ``` class DocumentationAdmonition < Base properties do + since_version "1.6.0" description "Reports documentation admonitions" admonitions %w[TODO FIXME BUG] timezone "UTC" diff --git a/src/ameba/rule/lint/formatting.cr b/src/ameba/rule/lint/formatting.cr index ea848ad25..8664261c2 100644 --- a/src/ameba/rule/lint/formatting.cr +++ b/src/ameba/rule/lint/formatting.cr @@ -27,6 +27,7 @@ module Ameba::Rule::Lint # ``` class Formatting < Base properties do + since_version "1.4.0" description "Reports not formatted sources" fail_on_error false end diff --git a/src/ameba/rule/lint/literal_assignments_in_expressions.cr b/src/ameba/rule/lint/literal_assignments_in_expressions.cr index 1851bb0f3..f030d380e 100644 --- a/src/ameba/rule/lint/literal_assignments_in_expressions.cr +++ b/src/ameba/rule/lint/literal_assignments_in_expressions.cr @@ -28,6 +28,7 @@ module Ameba::Rule::Lint include AST::Util properties do + since_version "1.4.0" description "Disallows assignments with literal values in control expressions" end diff --git a/src/ameba/rule/lint/missing_block_argument.cr b/src/ameba/rule/lint/missing_block_argument.cr index 5fd6176a6..26b612538 100644 --- a/src/ameba/rule/lint/missing_block_argument.cr +++ b/src/ameba/rule/lint/missing_block_argument.cr @@ -21,6 +21,7 @@ module Ameba::Rule::Lint # ``` class MissingBlockArgument < Base properties do + since_version "1.4.0" description "Disallows yielding method definitions without block argument" end diff --git a/src/ameba/rule/lint/spec_filename.cr b/src/ameba/rule/lint/spec_filename.cr index 32492a6fa..16316d21f 100644 --- a/src/ameba/rule/lint/spec_filename.cr +++ b/src/ameba/rule/lint/spec_filename.cr @@ -13,6 +13,7 @@ module Ameba::Rule::Lint # ``` class SpecFilename < Base properties do + since_version "1.6.0" description "Enforces spec filenames to have `_spec` suffix" ignored_dirs %w[spec/support spec/fixtures spec/data] ignored_filenames %w[spec_helper] diff --git a/src/ameba/rule/lint/typos.cr b/src/ameba/rule/lint/typos.cr index 567fa3af0..9cbb94dd4 100644 --- a/src/ameba/rule/lint/typos.cr +++ b/src/ameba/rule/lint/typos.cr @@ -14,6 +14,7 @@ module Ameba::Rule::Lint # ``` class Typos < Base properties do + since_version "1.6.0" description "Reports typos found in source files" bin_path nil, as: String? diff --git a/src/ameba/rule/lint/unused_block_argument.cr b/src/ameba/rule/lint/unused_block_argument.cr index 65a7255d5..6fa5067e2 100644 --- a/src/ameba/rule/lint/unused_block_argument.cr +++ b/src/ameba/rule/lint/unused_block_argument.cr @@ -32,6 +32,7 @@ module Ameba::Rule::Lint # ``` class UnusedBlockArgument < Base properties do + since_version "1.4.0" description "Disallows unused block arguments" end diff --git a/src/ameba/rule/naming/accessor_method_name.cr b/src/ameba/rule/naming/accessor_method_name.cr index fc082737e..b9ad2daf7 100644 --- a/src/ameba/rule/naming/accessor_method_name.cr +++ b/src/ameba/rule/naming/accessor_method_name.cr @@ -37,6 +37,7 @@ module Ameba::Rule::Naming # ``` class AccessorMethodName < Base properties do + since_version "1.6.0" description "Makes sure that accessor methods are named properly" end diff --git a/src/ameba/rule/naming/ascii_identifiers.cr b/src/ameba/rule/naming/ascii_identifiers.cr index f2739b47e..cf5cf5141 100644 --- a/src/ameba/rule/naming/ascii_identifiers.cr +++ b/src/ameba/rule/naming/ascii_identifiers.cr @@ -24,6 +24,7 @@ module Ameba::Rule::Naming # ``` class AsciiIdentifiers < Base properties do + since_version "1.6.0" description "Disallows non-ascii characters in identifiers" ignore_symbols false end diff --git a/src/ameba/rule/naming/binary_operator_parameter_name.cr b/src/ameba/rule/naming/binary_operator_parameter_name.cr index 16cc1ed6b..0a0f2d2cc 100644 --- a/src/ameba/rule/naming/binary_operator_parameter_name.cr +++ b/src/ameba/rule/naming/binary_operator_parameter_name.cr @@ -29,6 +29,7 @@ module Ameba::Rule::Naming # ``` class BinaryOperatorParameterName < Base properties do + since_version "1.6.0" description "Enforces that certain binary operator methods have " \ "their sole parameter named `other`" excluded_operators %w[[] []? []= << >> ` =~ !~] diff --git a/src/ameba/rule/naming/block_parameter_name.cr b/src/ameba/rule/naming/block_parameter_name.cr index f94be0d40..9c912d729 100644 --- a/src/ameba/rule/naming/block_parameter_name.cr +++ b/src/ameba/rule/naming/block_parameter_name.cr @@ -25,6 +25,7 @@ module Ameba::Rule::Naming # ``` class BlockParameterName < Base properties do + since_version "1.6.0" description "Disallows non-descriptive block parameter names" min_name_length 3 allow_names_ending_in_numbers true diff --git a/src/ameba/rule/naming/filename.cr b/src/ameba/rule/naming/filename.cr index 1c73b767b..0351221e1 100644 --- a/src/ameba/rule/naming/filename.cr +++ b/src/ameba/rule/naming/filename.cr @@ -9,6 +9,7 @@ module Ameba::Rule::Naming # ``` class Filename < Base properties do + since_version "1.6.0" description "Enforces file names to be in underscored case" end diff --git a/src/ameba/rule/naming/query_bool_methods.cr b/src/ameba/rule/naming/query_bool_methods.cr index 236e62211..b253f748f 100644 --- a/src/ameba/rule/naming/query_bool_methods.cr +++ b/src/ameba/rule/naming/query_bool_methods.cr @@ -30,6 +30,7 @@ module Ameba::Rule::Naming include AST::Util properties do + since_version "1.4.0" description "Reports boolean properties without the `?` suffix" end diff --git a/src/ameba/rule/naming/rescued_exceptions_variable_name.cr b/src/ameba/rule/naming/rescued_exceptions_variable_name.cr index f1230c517..91ddc56d2 100644 --- a/src/ameba/rule/naming/rescued_exceptions_variable_name.cr +++ b/src/ameba/rule/naming/rescued_exceptions_variable_name.cr @@ -26,6 +26,7 @@ module Ameba::Rule::Naming # ``` class RescuedExceptionsVariableName < Base properties do + since_version "1.6.0" description "Makes sure that rescued exceptions variables are named as expected" allowed_names %w[e ex exception error] end diff --git a/src/ameba/rule/performance/excessive_allocations.cr b/src/ameba/rule/performance/excessive_allocations.cr index e8677d741..9d38bf78f 100644 --- a/src/ameba/rule/performance/excessive_allocations.cr +++ b/src/ameba/rule/performance/excessive_allocations.cr @@ -33,6 +33,7 @@ module Ameba::Rule::Performance include AST::Util properties do + since_version "1.5.0" description "Identifies usage of excessive collection allocations" call_names({ "codepoints" => "each_codepoint", diff --git a/src/ameba/rule/performance/minmax_after_map.cr b/src/ameba/rule/performance/minmax_after_map.cr index 803c84e17..614fc7f36 100644 --- a/src/ameba/rule/performance/minmax_after_map.cr +++ b/src/ameba/rule/performance/minmax_after_map.cr @@ -29,6 +29,7 @@ module Ameba::Rule::Performance include AST::Util properties do + since_version "1.5.0" description "Identifies usage of `min/max/minmax` calls that follow `map`" end diff --git a/src/ameba/rule/style/parentheses_around_condition.cr b/src/ameba/rule/style/parentheses_around_condition.cr index 112d3a64c..45710b8fc 100644 --- a/src/ameba/rule/style/parentheses_around_condition.cr +++ b/src/ameba/rule/style/parentheses_around_condition.cr @@ -28,6 +28,7 @@ module Ameba::Rule::Style # ``` class ParenthesesAroundCondition < Base properties do + since_version "1.4.0" description "Disallows redundant parentheses around control expressions" exclude_ternary false From 2bec343d7e8877dd2524d1a7eaabd7e419a7048a Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Mon, 7 Oct 2024 21:19:32 +0200 Subject: [PATCH 03/16] Refactor `Runner#initialize` --- src/ameba/runner.cr | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/ameba/runner.cr b/src/ameba/runner.cr index b6edd01f5..e344692ea 100644 --- a/src/ameba/runner.cr +++ b/src/ameba/runner.cr @@ -62,23 +62,25 @@ module Ameba # Ameba::Runner.new config # ``` def initialize(config : Config) - @sources = config.sources - @formatter = config.formatter - @severity = config.severity - @version = config.version - @rules = config.rules.select do |rule| - rule.enabled? && !rule.special? && - (!(version = @version) || !(since_version = rule.since_version) || - since_version <= version) - end - @autocorrect = config.autocorrect? - - @unneeded_disable_directive_rule = - config.rules - .find &.class.==(Rule::Lint::UnneededDisableDirective) + initialize( + config.rules, + config.sources, + config.formatter, + config.severity, + config.autocorrect?, + config.version, + ) end - protected def initialize(@rules, @sources, @formatter, @severity, @autocorrect = false, @version = nil) + protected def initialize(rules, @sources, @formatter, @severity, @autocorrect = false, @version = nil) + @rules = + rules.select do |rule| + rule.enabled? && !rule.special? && + (!(version = @version) || !(since_version = rule.since_version) || + since_version <= version) + end + @unneeded_disable_directive_rule = + rules.find &.class.==(Rule::Lint::UnneededDisableDirective) end # Performs the inspection. Iterates through all sources and test it using From 74dfcb761ff047d43eb3f5cc06ba2fb25a10bdbe Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Mon, 7 Oct 2024 21:20:57 +0200 Subject: [PATCH 04/16] Add specs --- spec/ameba/cli/cmd_spec.cr | 7 +++++++ spec/ameba/config_spec.cr | 27 +++++++++++++++++++++++++++ spec/ameba/runner_spec.cr | 15 +++++++++++++++ spec/fixtures/config.yml | 2 ++ spec/spec_helper.cr | 11 +++++++++++ 5 files changed, 62 insertions(+) diff --git a/spec/ameba/cli/cmd_spec.cr b/spec/ameba/cli/cmd_spec.cr index 5270133e3..0f174243e 100644 --- a/spec/ameba/cli/cmd_spec.cr +++ b/spec/ameba/cli/cmd_spec.cr @@ -32,6 +32,13 @@ module Ameba::Cli end end + %w[-u --up-to-version].each do |flag| + it "accepts #{flag} flag" do + c = Cli.parse_args [flag, "1.5.0"] + c.version.should eq "1.5.0" + end + end + it "accepts --stdin-filename flag" do c = Cli.parse_args %w[--stdin-filename foo.cr] c.stdin_filename.should eq "foo.cr" diff --git a/spec/ameba/config_spec.cr b/spec/ameba/config_spec.cr index d061d2e0e..b1a5878c0 100644 --- a/spec/ameba/config_spec.cr +++ b/spec/ameba/config_spec.cr @@ -95,6 +95,7 @@ module Ameba it "loads custom config" do config = Config.load config_sample config.should_not be_nil + config.version.should_not be_nil config.globs.should_not be_nil config.formatter.should_not be_nil end @@ -108,6 +109,7 @@ module Ameba it "loads default config" do config = Config.load config.should_not be_nil + config.version.should be_nil config.globs.should_not be_nil config.formatter.should_not be_nil end @@ -184,6 +186,31 @@ module Ameba end end + describe "#version, version=" do + config = Config.load config_sample + version = SemanticVersion.parse("1.5.0") + + it "contains default version" do + config.version.should_not be_nil + end + + it "allows to set version" do + config.version = version + config.version.should eq version + end + + it "allows to set version using a string" do + config.version = version.to_s + config.version.should eq version + end + + it "raises an error if version is not valid" do + expect_raises(Exception) do + config.version = "foo" + end + end + end + describe "#update_rule" do config = Config.load config_sample diff --git a/spec/ameba/runner_spec.cr b/spec/ameba/runner_spec.cr index f1fc904cf..17aa97c8c 100644 --- a/spec/ameba/runner_spec.cr +++ b/spec/ameba/runner_spec.cr @@ -6,6 +6,7 @@ module Ameba config.formatter = formatter config.globs = files + config.update_rule VersionedRule.rule_name, enabled: false config.update_rule ErrorRule.rule_name, enabled: false config.update_rule PerfRule.rule_name, enabled: false config.update_rule AtoAA.rule_name, enabled: false @@ -48,6 +49,20 @@ module Ameba formatter.finished_source.should_not be_nil end + it "checks accordingly to the rule #since_version" do + rules = [VersionedRule.new] of Rule::Base + source = Source.new "", "source.cr" + + v1_0_0 = SemanticVersion.parse("1.0.0") + Runner.new(rules, [source], formatter, default_severity, false, v1_0_0).run.success?.should be_true + + v1_5_0 = SemanticVersion.parse("1.5.0") + Runner.new(rules, [source], formatter, default_severity, false, v1_5_0).run.success?.should be_false + + v1_10_0 = SemanticVersion.parse("1.10.0") + Runner.new(rules, [source], formatter, default_severity, false, v1_10_0).run.success?.should be_false + end + it "skips rule check if source is excluded" do path = "source.cr" source = Source.new "", path diff --git a/spec/fixtures/config.yml b/spec/fixtures/config.yml index c5394b90f..07efce09a 100644 --- a/spec/fixtures/config.yml +++ b/spec/fixtures/config.yml @@ -1,2 +1,4 @@ +version: "1.5.0" + Lint/ComparisonToBoolean: Enabled: true diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 8f0c3b928..acfcc3dac 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -24,6 +24,17 @@ module Ameba end end + class VersionedRule < Rule::Base + properties do + since_version "1.5.0" + description "Rule with a custom version." + end + + def test(source) + issue_for({1, 1}, "This rule always adds an error") + end + end + # Rule extended description class ErrorRule < Rule::Base properties do From d497e3228e168a5426adae41ded8d5f50e6ab8ad Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Mon, 7 Oct 2024 23:42:16 +0200 Subject: [PATCH 05/16] Extract rule selection logic into its own helper method --- src/ameba/runner.cr | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ameba/runner.cr b/src/ameba/runner.cr index e344692ea..cc7aa66dc 100644 --- a/src/ameba/runner.cr +++ b/src/ameba/runner.cr @@ -74,15 +74,17 @@ module Ameba protected def initialize(rules, @sources, @formatter, @severity, @autocorrect = false, @version = nil) @rules = - rules.select do |rule| - rule.enabled? && !rule.special? && - (!(version = @version) || !(since_version = rule.since_version) || - since_version <= version) - end + rules.select { |rule| rule_runnable?(rule) } @unneeded_disable_directive_rule = rules.find &.class.==(Rule::Lint::UnneededDisableDirective) end + protected def rule_runnable?(rule) + rule.enabled? && !rule.special? && + (!(version = @version) || !(since_version = rule.since_version) || + since_version <= version) + end + # Performs the inspection. Iterates through all sources and test it using # list of rules. If a specific rule fails on a specific source, it adds # an issue to that source. From 83972c520e968de0cdc940c68b5559403714fbfd Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Tue, 8 Oct 2024 00:11:38 +0200 Subject: [PATCH 06/16] Add `--rule-versions` CLI switch to show all available versions --- src/ameba/cli/cmd.cr | 11 +++++++++++ src/ameba/presenter/rule_versions_presenter.cr | 12 ++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/ameba/presenter/rule_versions_presenter.cr diff --git a/src/ameba/cli/cmd.cr b/src/ameba/cli/cmd.cr index b4a5c070b..769492554 100644 --- a/src/ameba/cli/cmd.cr +++ b/src/ameba/cli/cmd.cr @@ -41,6 +41,10 @@ module Ameba::Cli print_rules(config.rules) end + if opts.rule_versions? + print_rule_versions(config.rules) + end + if describe_rule_name = opts.describe_rule unless rule = config.rules.find(&.name.== describe_rule_name) raise "Unknown rule" @@ -73,6 +77,7 @@ module Ameba::Cli property stdin_filename : String? property? skip_reading_config = false property? rules = false + property? rule_versions = false property? all = false property? colors = true property? without_affected_code = false @@ -86,6 +91,7 @@ module Ameba::Cli parser.on("-v", "--version", "Print version") { print_version } parser.on("-h", "--help", "Show this help") { print_help(parser) } parser.on("-r", "--rules", "Show all available rules") { opts.rules = true } + parser.on("-R", "--rule-versions", "Show all available rule versions") { opts.rule_versions = true } parser.on("-s", "--silent", "Disable output") { opts.formatter = :silent } parser.unknown_args do |arr| case @@ -234,4 +240,9 @@ module Ameba::Cli Presenter::RuleCollectionPresenter.new.run(rules) exit 0 end + + private def print_rule_versions(rules) + Presenter::RuleVersionsPresenter.new.run(rules) + exit 0 + end end diff --git a/src/ameba/presenter/rule_versions_presenter.cr b/src/ameba/presenter/rule_versions_presenter.cr new file mode 100644 index 000000000..4c821f542 --- /dev/null +++ b/src/ameba/presenter/rule_versions_presenter.cr @@ -0,0 +1,12 @@ +module Ameba::Presenter + class RuleVersionsPresenter < BasePresenter + def run(rules) + versions = + rules.compact_map(&.since_version).sort!.uniq! + + versions.each do |version| + output.puts "- %s" % version.to_s.colorize(:green) + end + end + end +end From 9e5b3f2a509e52b0e61174e64cb37b0bcb38bfdb Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Tue, 8 Oct 2024 00:21:02 +0200 Subject: [PATCH 07/16] Extract config creation from CLI options into a separate helper method --- src/ameba/cli/cmd.cr | 48 +++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/ameba/cli/cmd.cr b/src/ameba/cli/cmd.cr index 769492554..88a4724f8 100644 --- a/src/ameba/cli/cmd.cr +++ b/src/ameba/cli/cmd.cr @@ -5,37 +5,18 @@ require "option_parser" module Ameba::Cli extend self - # ameba:disable Metrics/CyclomaticComplexity def run(args = ARGV) : Nil - opts = parse_args args - location_to_explain = opts.location_to_explain - stdin_filename = opts.stdin_filename - autocorrect = opts.autocorrect? + opts = parse_args(args) - if location_to_explain && autocorrect + if (location_to_explain = opts.location_to_explain) && opts.autocorrect? raise "Invalid usage: Cannot explain an issue and autocorrect at the same time." end - if stdin_filename && autocorrect + if opts.stdin_filename && opts.autocorrect? raise "Invalid usage: Cannot autocorrect from stdin." end - config = Config.load opts.config, opts.colors?, opts.skip_reading_config? - config.autocorrect = autocorrect - config.stdin_filename = stdin_filename - - if version = opts.version - config.version = version - end - if globs = opts.globs - config.globs = globs - end - if fail_level = opts.fail_level - config.severity = fail_level - end - - configure_formatter(config, opts) - configure_rules(config, opts) + config = config_from_opts(opts) if opts.rules? print_rules(config.rules) @@ -175,6 +156,27 @@ module Ameba::Cli opts end + private def config_from_opts(opts) + config = Config.load opts.config, opts.colors?, opts.skip_reading_config? + config.autocorrect = opts.autocorrect? + config.stdin_filename = opts.stdin_filename + + if version = opts.version + config.version = version + end + if globs = opts.globs + config.globs = globs + end + if fail_level = opts.fail_level + config.severity = fail_level + end + + configure_formatter(config, opts) + configure_rules(config, opts) + + config + end + private def configure_rules(config, opts) : Nil case when only = opts.only From 982143c41b20c342300e04f17989ae03f6dcd46b Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Tue, 8 Oct 2024 00:35:55 +0200 Subject: [PATCH 08/16] Move `Cli::Opts` definition moar to the top --- src/ameba/cli/cmd.cr | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ameba/cli/cmd.cr b/src/ameba/cli/cmd.cr index 88a4724f8..16732b906 100644 --- a/src/ameba/cli/cmd.cr +++ b/src/ameba/cli/cmd.cr @@ -5,6 +5,26 @@ require "option_parser" module Ameba::Cli extend self + private class Opts + property config : Path? + property version : String? + property formatter : Symbol | String | Nil + property globs : Array(String)? + property only : Array(String)? + property except : Array(String)? + property describe_rule : String? + property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)? + property fail_level : Severity? + property stdin_filename : String? + property? skip_reading_config = false + property? rules = false + property? rule_versions = false + property? all = false + property? colors = true + property? without_affected_code = false + property? autocorrect = false + end + def run(args = ARGV) : Nil opts = parse_args(args) @@ -45,26 +65,6 @@ module Ameba::Cli exit 255 end - private class Opts - property config : Path? - property version : String? - property formatter : Symbol | String | Nil - property globs : Array(String)? - property only : Array(String)? - property except : Array(String)? - property describe_rule : String? - property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)? - property fail_level : Severity? - property stdin_filename : String? - property? skip_reading_config = false - property? rules = false - property? rule_versions = false - property? all = false - property? colors = true - property? without_affected_code = false - property? autocorrect = false - end - def parse_args(args, opts = Opts.new) OptionParser.parse(args) do |parser| parser.banner = "Usage: ameba [options] [file1 file2 ...]" From 3a12ee0f1320e219d026f17a51af057961d10cec Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Tue, 8 Oct 2024 02:23:12 +0200 Subject: [PATCH 09/16] Refactor `RulePresenter` to include `Rule#since_version` --- src/ameba/presenter/rule_presenter.cr | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/ameba/presenter/rule_presenter.cr b/src/ameba/presenter/rule_presenter.cr index 9c7da467a..ef35f21a1 100644 --- a/src/ameba/presenter/rule_presenter.cr +++ b/src/ameba/presenter/rule_presenter.cr @@ -1,14 +1,24 @@ module Ameba::Presenter class RulePresenter < BasePresenter def run(rule) : Nil - output.puts output_title "Rule info" - output_paragraph "%s of a %s severity [enabled: %s]" % { + + info = <<-INFO + Name: %s + Severity: %s + Enabled: %s + Since version: %s + INFO + + output_paragraph info % { rule.name.colorize(:magenta), rule.severity.to_s.colorize(rule.severity.color), rule.enabled? ? ENABLED_MARK : DISABLED_MARK, + (rule.since_version.try(&.to_s) || "N/A").colorize(:white), } + if rule_description = colorize_code_fences(rule.description) + output_title "Description" output_paragraph rule_description end From ca3bf13a75be78c4f346cd909167944b74ea84a4 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Tue, 8 Oct 2024 21:19:07 +0200 Subject: [PATCH 10/16] Use `Version` as a YAML key in `.ameba.yml` --- spec/fixtures/config.yml | 2 +- src/ameba/config.cr | 2 +- src/ameba/formatter/todo_formatter.cr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/fixtures/config.yml b/spec/fixtures/config.yml index 07efce09a..4a95fa4ad 100644 --- a/spec/fixtures/config.yml +++ b/spec/fixtures/config.yml @@ -1,4 +1,4 @@ -version: "1.5.0" +Version: "1.5.0" Lint/ComparisonToBoolean: Enabled: true diff --git a/src/ameba/config.cr b/src/ameba/config.cr index 95fc20253..2c0b84f8e 100644 --- a/src/ameba/config.cr +++ b/src/ameba/config.cr @@ -109,7 +109,7 @@ class Ameba::Config @excluded = load_array_section(config, "Excluded") @globs = load_array_section(config, "Globs", DEFAULT_GLOBS) - if version = config["version"]?.try(&.as_s).presence + if version = config["Version"]?.try(&.as_s).presence self.version = version end if formatter_name = load_formatter_name(config) diff --git a/src/ameba/formatter/todo_formatter.cr b/src/ameba/formatter/todo_formatter.cr index 00dc085c8..139bc2630 100644 --- a/src/ameba/formatter/todo_formatter.cr +++ b/src/ameba/formatter/todo_formatter.cr @@ -62,7 +62,7 @@ module Ameba::Formatter # The point is for the user to remove these configuration records # one by one as the reported problems are removed from the code base. - version: "#{VERSION}" + Version: "#{VERSION}" HEADER end From d7edbfef3ada934d7dff9b0bdb41474e6cd4efdd Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Wed, 9 Oct 2024 02:06:14 +0200 Subject: [PATCH 11/16] Version remaining rules --- src/ameba/rule/layout/line_length.cr | 1 + src/ameba/rule/layout/trailing_blank_lines.cr | 1 + src/ameba/rule/layout/trailing_whitespace.cr | 1 + src/ameba/rule/lint/ambiguous_assignment.cr | 1 + src/ameba/rule/lint/bad_directive.cr | 1 + src/ameba/rule/lint/comparison_to_boolean.cr | 1 + src/ameba/rule/lint/debug_calls.cr | 1 + src/ameba/rule/lint/debugger_statement.cr | 1 + src/ameba/rule/lint/duplicated_require.cr | 1 + src/ameba/rule/lint/empty_ensure.cr | 1 + src/ameba/rule/lint/empty_expression.cr | 1 + src/ameba/rule/lint/empty_loop.cr | 1 + src/ameba/rule/lint/hash_duplicated_key.cr | 1 + src/ameba/rule/lint/literal_in_condition.cr | 1 + src/ameba/rule/lint/literal_in_interpolation.cr | 1 + src/ameba/rule/lint/literals_comparison.cr | 1 + src/ameba/rule/lint/not_nil.cr | 1 + src/ameba/rule/lint/not_nil_after_no_bang.cr | 1 + src/ameba/rule/lint/percent_array.cr | 1 + src/ameba/rule/lint/rand_zero.cr | 1 + src/ameba/rule/lint/redundant_string_coercion.cr | 1 + src/ameba/rule/lint/redundant_with_index.cr | 1 + src/ameba/rule/lint/redundant_with_object.cr | 1 + src/ameba/rule/lint/shadowed_argument.cr | 1 + src/ameba/rule/lint/shadowed_exception.cr | 1 + src/ameba/rule/lint/shadowing_outer_local_var.cr | 1 + src/ameba/rule/lint/shared_var_in_fiber.cr | 1 + src/ameba/rule/lint/spec_focus.cr | 1 + src/ameba/rule/lint/syntax.cr | 1 + src/ameba/rule/lint/unneeded_disable_directive.cr | 1 + src/ameba/rule/lint/unreachable_code.cr | 1 + src/ameba/rule/lint/unused_argument.cr | 1 + src/ameba/rule/lint/useless_assign.cr | 1 + src/ameba/rule/lint/useless_condition_in_when.cr | 1 + src/ameba/rule/metrics/cyclomatic_complexity.cr | 1 + src/ameba/rule/naming/constant_names.cr | 1 + src/ameba/rule/naming/method_names.cr | 1 + src/ameba/rule/naming/predicate_name.cr | 1 + src/ameba/rule/naming/type_names.cr | 1 + src/ameba/rule/naming/variable_names.cr | 1 + src/ameba/rule/performance/any_after_filter.cr | 1 + src/ameba/rule/performance/any_instead_of_empty.cr | 1 + src/ameba/rule/performance/chained_call_with_no_bang.cr | 1 + src/ameba/rule/performance/compact_after_map.cr | 1 + src/ameba/rule/performance/first_last_after_filter.cr | 1 + src/ameba/rule/performance/flatten_after_map.cr | 1 + src/ameba/rule/performance/map_instead_of_block.cr | 1 + src/ameba/rule/performance/size_after_filter.cr | 1 + src/ameba/rule/style/guard_clause.cr | 1 + src/ameba/rule/style/is_a_filter.cr | 1 + src/ameba/rule/style/is_a_nil.cr | 1 + src/ameba/rule/style/large_numbers.cr | 1 + src/ameba/rule/style/negated_conditions_in_unless.cr | 1 + src/ameba/rule/style/redundant_begin.cr | 1 + src/ameba/rule/style/redundant_next.cr | 1 + src/ameba/rule/style/redundant_return.cr | 1 + src/ameba/rule/style/unless_else.cr | 1 + src/ameba/rule/style/verbose_block.cr | 1 + src/ameba/rule/style/while_true.cr | 1 + 59 files changed, 59 insertions(+) diff --git a/src/ameba/rule/layout/line_length.cr b/src/ameba/rule/layout/line_length.cr index 41eb76fa4..a3b49e22d 100644 --- a/src/ameba/rule/layout/line_length.cr +++ b/src/ameba/rule/layout/line_length.cr @@ -10,6 +10,7 @@ module Ameba::Rule::Layout # ``` class LineLength < Base properties do + since_version "0.1.0" enabled false description "Disallows lines longer than `MaxLength` number of symbols" max_length 140 diff --git a/src/ameba/rule/layout/trailing_blank_lines.cr b/src/ameba/rule/layout/trailing_blank_lines.cr index dbfa3aa68..b8b727d8c 100644 --- a/src/ameba/rule/layout/trailing_blank_lines.cr +++ b/src/ameba/rule/layout/trailing_blank_lines.cr @@ -9,6 +9,7 @@ module Ameba::Rule::Layout # ``` class TrailingBlankLines < Base properties do + since_version "0.1.0" description "Disallows trailing blank lines" end diff --git a/src/ameba/rule/layout/trailing_whitespace.cr b/src/ameba/rule/layout/trailing_whitespace.cr index 48561a966..83f6badd1 100644 --- a/src/ameba/rule/layout/trailing_whitespace.cr +++ b/src/ameba/rule/layout/trailing_whitespace.cr @@ -9,6 +9,7 @@ module Ameba::Rule::Layout # ``` class TrailingWhitespace < Base properties do + since_version "0.1.0" description "Disallows trailing whitespace" end diff --git a/src/ameba/rule/lint/ambiguous_assignment.cr b/src/ameba/rule/lint/ambiguous_assignment.cr index f0f1bd7a5..6cacc0186 100644 --- a/src/ameba/rule/lint/ambiguous_assignment.cr +++ b/src/ameba/rule/lint/ambiguous_assignment.cr @@ -27,6 +27,7 @@ module Ameba::Rule::Lint include AST::Util properties do + since_version "1.0.0" description "Disallows ambiguous `=-/=+/=!`" end diff --git a/src/ameba/rule/lint/bad_directive.cr b/src/ameba/rule/lint/bad_directive.cr index e0ff30f41..7a207b722 100644 --- a/src/ameba/rule/lint/bad_directive.cr +++ b/src/ameba/rule/lint/bad_directive.cr @@ -19,6 +19,7 @@ module Ameba::Rule::Lint # ``` class BadDirective < Base properties do + since_version "0.13.0" description "Reports bad comment directives" end diff --git a/src/ameba/rule/lint/comparison_to_boolean.cr b/src/ameba/rule/lint/comparison_to_boolean.cr index abd4efc74..b669f859e 100644 --- a/src/ameba/rule/lint/comparison_to_boolean.cr +++ b/src/ameba/rule/lint/comparison_to_boolean.cr @@ -23,6 +23,7 @@ module Ameba::Rule::Lint include AST::Util properties do + since_version "0.1.0" enabled false description "Disallows comparison to booleans" end diff --git a/src/ameba/rule/lint/debug_calls.cr b/src/ameba/rule/lint/debug_calls.cr index b2958295b..217ab1652 100644 --- a/src/ameba/rule/lint/debug_calls.cr +++ b/src/ameba/rule/lint/debug_calls.cr @@ -17,6 +17,7 @@ module Ameba::Rule::Lint # ``` class DebugCalls < Base properties do + since_version "1.0.0" description "Disallows debug-related calls" method_names %w[p p! pp pp!] end diff --git a/src/ameba/rule/lint/debugger_statement.cr b/src/ameba/rule/lint/debugger_statement.cr index da478cbb4..721dfae18 100644 --- a/src/ameba/rule/lint/debugger_statement.cr +++ b/src/ameba/rule/lint/debugger_statement.cr @@ -12,6 +12,7 @@ module Ameba::Rule::Lint # ``` class DebuggerStatement < Base properties do + since_version "0.1.0" description "Disallows calls to debugger" end diff --git a/src/ameba/rule/lint/duplicated_require.cr b/src/ameba/rule/lint/duplicated_require.cr index d1fe12954..fcbc64efc 100644 --- a/src/ameba/rule/lint/duplicated_require.cr +++ b/src/ameba/rule/lint/duplicated_require.cr @@ -15,6 +15,7 @@ module Ameba::Rule::Lint # ``` class DuplicatedRequire < Base properties do + since_version "0.14.0" description "Reports duplicated require statements" end diff --git a/src/ameba/rule/lint/empty_ensure.cr b/src/ameba/rule/lint/empty_ensure.cr index dce5dfae4..fc08f3fc2 100644 --- a/src/ameba/rule/lint/empty_ensure.cr +++ b/src/ameba/rule/lint/empty_ensure.cr @@ -39,6 +39,7 @@ module Ameba::Rule::Lint # ``` class EmptyEnsure < Base properties do + since_version "0.3.0" description "Disallows empty ensure statement" end diff --git a/src/ameba/rule/lint/empty_expression.cr b/src/ameba/rule/lint/empty_expression.cr index bac570c97..3fdb779b6 100644 --- a/src/ameba/rule/lint/empty_expression.cr +++ b/src/ameba/rule/lint/empty_expression.cr @@ -29,6 +29,7 @@ module Ameba::Rule::Lint # ``` class EmptyExpression < Base properties do + since_version "0.2.0" description "Disallows empty expressions" end diff --git a/src/ameba/rule/lint/empty_loop.cr b/src/ameba/rule/lint/empty_loop.cr index 32d9b5a4c..0752d700c 100644 --- a/src/ameba/rule/lint/empty_loop.cr +++ b/src/ameba/rule/lint/empty_loop.cr @@ -41,6 +41,7 @@ module Ameba::Rule::Lint include AST::Util properties do + since_version "0.12.0" description "Disallows empty loops" end diff --git a/src/ameba/rule/lint/hash_duplicated_key.cr b/src/ameba/rule/lint/hash_duplicated_key.cr index 5b335edc6..3843a9c11 100644 --- a/src/ameba/rule/lint/hash_duplicated_key.cr +++ b/src/ameba/rule/lint/hash_duplicated_key.cr @@ -21,6 +21,7 @@ module Ameba::Rule::Lint # ``` class HashDuplicatedKey < Base properties do + since_version "0.3.0" description "Disallows duplicated keys in hash literals" end diff --git a/src/ameba/rule/lint/literal_in_condition.cr b/src/ameba/rule/lint/literal_in_condition.cr index 3be1c152f..bf111c56f 100644 --- a/src/ameba/rule/lint/literal_in_condition.cr +++ b/src/ameba/rule/lint/literal_in_condition.cr @@ -24,6 +24,7 @@ module Ameba::Rule::Lint include AST::Util properties do + since_version "0.1.0" description "Disallows useless conditional statements that contain \ a literal in place of a variable or predicate function" end diff --git a/src/ameba/rule/lint/literal_in_interpolation.cr b/src/ameba/rule/lint/literal_in_interpolation.cr index e5bbbd3f3..61bfae50e 100644 --- a/src/ameba/rule/lint/literal_in_interpolation.cr +++ b/src/ameba/rule/lint/literal_in_interpolation.cr @@ -19,6 +19,7 @@ module Ameba::Rule::Lint include AST::Util properties do + since_version "0.1.0" description "Disallows useless string interpolations" end diff --git a/src/ameba/rule/lint/literals_comparison.cr b/src/ameba/rule/lint/literals_comparison.cr index 9f9a7fee6..cffd4b293 100644 --- a/src/ameba/rule/lint/literals_comparison.cr +++ b/src/ameba/rule/lint/literals_comparison.cr @@ -20,6 +20,7 @@ module Ameba::Rule::Lint include AST::Util properties do + since_version "1.3.0" description "Identifies comparisons between literals" end diff --git a/src/ameba/rule/lint/not_nil.cr b/src/ameba/rule/lint/not_nil.cr index 1ab405e2e..1ac07d1d4 100644 --- a/src/ameba/rule/lint/not_nil.cr +++ b/src/ameba/rule/lint/not_nil.cr @@ -27,6 +27,7 @@ module Ameba::Rule::Lint # ``` class NotNil < Base properties do + since_version "1.3.0" description "Identifies usage of `not_nil!` calls" end diff --git a/src/ameba/rule/lint/not_nil_after_no_bang.cr b/src/ameba/rule/lint/not_nil_after_no_bang.cr index 1f9f3394c..3c4444408 100644 --- a/src/ameba/rule/lint/not_nil_after_no_bang.cr +++ b/src/ameba/rule/lint/not_nil_after_no_bang.cr @@ -24,6 +24,7 @@ module Ameba::Rule::Lint include AST::Util properties do + since_version "1.3.0" description "Identifies usage of `index/rindex/find/match` calls followed by `not_nil!`" end diff --git a/src/ameba/rule/lint/percent_array.cr b/src/ameba/rule/lint/percent_array.cr index 987d87d80..d47aa7951 100644 --- a/src/ameba/rule/lint/percent_array.cr +++ b/src/ameba/rule/lint/percent_array.cr @@ -25,6 +25,7 @@ module Ameba::Rule::Lint # ``` class PercentArrays < Base properties do + since_version "0.3.0" description "Disallows some unwanted symbols in percent array literals" string_array_unwanted_symbols %(,") diff --git a/src/ameba/rule/lint/rand_zero.cr b/src/ameba/rule/lint/rand_zero.cr index 5293704ff..9902c744e 100644 --- a/src/ameba/rule/lint/rand_zero.cr +++ b/src/ameba/rule/lint/rand_zero.cr @@ -24,6 +24,7 @@ module Ameba::Rule::Lint # ``` class RandZero < Base properties do + since_version "0.5.1" description "Disallows rand zero calls" end diff --git a/src/ameba/rule/lint/redundant_string_coercion.cr b/src/ameba/rule/lint/redundant_string_coercion.cr index 2dc4254be..ba39cb0ba 100644 --- a/src/ameba/rule/lint/redundant_string_coercion.cr +++ b/src/ameba/rule/lint/redundant_string_coercion.cr @@ -24,6 +24,7 @@ module Ameba::Rule::Lint include AST::Util properties do + since_version "0.12.0" description "Disallows redundant string conversions in interpolation" end diff --git a/src/ameba/rule/lint/redundant_with_index.cr b/src/ameba/rule/lint/redundant_with_index.cr index ac42e4f8f..bbedcac61 100644 --- a/src/ameba/rule/lint/redundant_with_index.cr +++ b/src/ameba/rule/lint/redundant_with_index.cr @@ -29,6 +29,7 @@ module Ameba::Rule::Lint # ``` class RedundantWithIndex < Base properties do + since_version "0.11.0" description "Disallows redundant `with_index` calls" end diff --git a/src/ameba/rule/lint/redundant_with_object.cr b/src/ameba/rule/lint/redundant_with_object.cr index 7dfbb968c..c02e20529 100644 --- a/src/ameba/rule/lint/redundant_with_object.cr +++ b/src/ameba/rule/lint/redundant_with_object.cr @@ -29,6 +29,7 @@ module Ameba::Rule::Lint # ``` class RedundantWithObject < Base properties do + since_version "0.11.0" description "Disallows redundant `with_object` calls" end diff --git a/src/ameba/rule/lint/shadowed_argument.cr b/src/ameba/rule/lint/shadowed_argument.cr index c21c1daed..778fe5e1c 100644 --- a/src/ameba/rule/lint/shadowed_argument.cr +++ b/src/ameba/rule/lint/shadowed_argument.cr @@ -37,6 +37,7 @@ module Ameba::Rule::Lint # ``` class ShadowedArgument < Base properties do + since_version "0.7.0" description "Disallows shadowed arguments" end diff --git a/src/ameba/rule/lint/shadowed_exception.cr b/src/ameba/rule/lint/shadowed_exception.cr index b6708a245..b9054aaf5 100644 --- a/src/ameba/rule/lint/shadowed_exception.cr +++ b/src/ameba/rule/lint/shadowed_exception.cr @@ -35,6 +35,7 @@ module Ameba::Rule::Lint # ``` class ShadowedException < Base properties do + since_version "0.3.0" description "Disallows rescued exception that get shadowed" end diff --git a/src/ameba/rule/lint/shadowing_outer_local_var.cr b/src/ameba/rule/lint/shadowing_outer_local_var.cr index 672e0579b..0d587a048 100644 --- a/src/ameba/rule/lint/shadowing_outer_local_var.cr +++ b/src/ameba/rule/lint/shadowing_outer_local_var.cr @@ -32,6 +32,7 @@ module Ameba::Rule::Lint # ``` class ShadowingOuterLocalVar < Base properties do + since_version "0.7.0" description "Disallows the usage of the same name as outer local variables " \ "for block or proc arguments" end diff --git a/src/ameba/rule/lint/shared_var_in_fiber.cr b/src/ameba/rule/lint/shared_var_in_fiber.cr index c68c2d4b3..b9bc50775 100644 --- a/src/ameba/rule/lint/shared_var_in_fiber.cr +++ b/src/ameba/rule/lint/shared_var_in_fiber.cr @@ -51,6 +51,7 @@ module Ameba::Rule::Lint # ``` class SharedVarInFiber < Base properties do + since_version "0.12.0" description "Disallows shared variables in fibers" end diff --git a/src/ameba/rule/lint/spec_focus.cr b/src/ameba/rule/lint/spec_focus.cr index b165145a1..51839117f 100644 --- a/src/ameba/rule/lint/spec_focus.cr +++ b/src/ameba/rule/lint/spec_focus.cr @@ -46,6 +46,7 @@ module Ameba::Rule::Lint # ``` class SpecFocus < Base properties do + since_version "0.14.0" description "Reports focused spec items" end diff --git a/src/ameba/rule/lint/syntax.cr b/src/ameba/rule/lint/syntax.cr index 14e7ba4bf..c135c9a3a 100644 --- a/src/ameba/rule/lint/syntax.cr +++ b/src/ameba/rule/lint/syntax.cr @@ -20,6 +20,7 @@ module Ameba::Rule::Lint # ``` class Syntax < Base properties do + since_version "0.4.2" description "Reports invalid Crystal syntax" severity :error end diff --git a/src/ameba/rule/lint/unneeded_disable_directive.cr b/src/ameba/rule/lint/unneeded_disable_directive.cr index a32215a81..5c447cbf1 100644 --- a/src/ameba/rule/lint/unneeded_disable_directive.cr +++ b/src/ameba/rule/lint/unneeded_disable_directive.cr @@ -26,6 +26,7 @@ module Ameba::Rule::Lint # ``` class UnneededDisableDirective < Base properties do + since_version "0.5.0" description "Reports unneeded disable directives in comments" end diff --git a/src/ameba/rule/lint/unreachable_code.cr b/src/ameba/rule/lint/unreachable_code.cr index f3c28439c..b1441aa35 100644 --- a/src/ameba/rule/lint/unreachable_code.cr +++ b/src/ameba/rule/lint/unreachable_code.cr @@ -43,6 +43,7 @@ module Ameba::Rule::Lint # ``` class UnreachableCode < Base properties do + since_version "0.9.0" description "Reports unreachable code" end diff --git a/src/ameba/rule/lint/unused_argument.cr b/src/ameba/rule/lint/unused_argument.cr index d001a1aad..7adae8b6c 100644 --- a/src/ameba/rule/lint/unused_argument.cr +++ b/src/ameba/rule/lint/unused_argument.cr @@ -27,6 +27,7 @@ module Ameba::Rule::Lint # ``` class UnusedArgument < Base properties do + since_version "0.6.0" description "Disallows unused arguments" ignore_defs true diff --git a/src/ameba/rule/lint/useless_assign.cr b/src/ameba/rule/lint/useless_assign.cr index 6e59e7ced..0b584f7af 100644 --- a/src/ameba/rule/lint/useless_assign.cr +++ b/src/ameba/rule/lint/useless_assign.cr @@ -28,6 +28,7 @@ module Ameba::Rule::Lint # ``` class UselessAssign < Base properties do + since_version "0.6.0" description "Disallows useless variable assignments" exclude_type_declarations false end diff --git a/src/ameba/rule/lint/useless_condition_in_when.cr b/src/ameba/rule/lint/useless_condition_in_when.cr index da3f47abe..00c0f3ec6 100644 --- a/src/ameba/rule/lint/useless_condition_in_when.cr +++ b/src/ameba/rule/lint/useless_condition_in_when.cr @@ -32,6 +32,7 @@ module Ameba::Rule::Lint # ``` class UselessConditionInWhen < Base properties do + since_version "0.3.0" description "Disallows useless conditions in when" end diff --git a/src/ameba/rule/metrics/cyclomatic_complexity.cr b/src/ameba/rule/metrics/cyclomatic_complexity.cr index 51d6abce4..38be96240 100644 --- a/src/ameba/rule/metrics/cyclomatic_complexity.cr +++ b/src/ameba/rule/metrics/cyclomatic_complexity.cr @@ -10,6 +10,7 @@ module Ameba::Rule::Metrics # ``` class CyclomaticComplexity < Base properties do + since_version "0.9.1" description "Disallows methods with a cyclomatic complexity higher than `MaxComplexity`" max_complexity 10 end diff --git a/src/ameba/rule/naming/constant_names.cr b/src/ameba/rule/naming/constant_names.cr index 88f815fbd..5995fe4c5 100644 --- a/src/ameba/rule/naming/constant_names.cr +++ b/src/ameba/rule/naming/constant_names.cr @@ -23,6 +23,7 @@ module Ameba::Rule::Naming # ``` class ConstantNames < Base properties do + since_version "0.2.0" description "Enforces constant names to be in screaming case" end diff --git a/src/ameba/rule/naming/method_names.cr b/src/ameba/rule/naming/method_names.cr index d434d9a7a..a90a8606a 100644 --- a/src/ameba/rule/naming/method_names.cr +++ b/src/ameba/rule/naming/method_names.cr @@ -39,6 +39,7 @@ module Ameba::Rule::Naming # ``` class MethodNames < Base properties do + since_version "0.2.0" description "Enforces method names to be in underscored case" end diff --git a/src/ameba/rule/naming/predicate_name.cr b/src/ameba/rule/naming/predicate_name.cr index b3935f2be..c69af0c9f 100644 --- a/src/ameba/rule/naming/predicate_name.cr +++ b/src/ameba/rule/naming/predicate_name.cr @@ -25,6 +25,7 @@ module Ameba::Rule::Naming # ``` class PredicateName < Base properties do + since_version "0.2.0" description "Disallows tautological predicate names" end diff --git a/src/ameba/rule/naming/type_names.cr b/src/ameba/rule/naming/type_names.cr index fb472471f..bdfb3d624 100644 --- a/src/ameba/rule/naming/type_names.cr +++ b/src/ameba/rule/naming/type_names.cr @@ -53,6 +53,7 @@ module Ameba::Rule::Naming # ``` class TypeNames < Base properties do + since_version "0.2.0" description "Enforces type names in camelcase manner" end diff --git a/src/ameba/rule/naming/variable_names.cr b/src/ameba/rule/naming/variable_names.cr index fe28b3ebb..a35eb448b 100644 --- a/src/ameba/rule/naming/variable_names.cr +++ b/src/ameba/rule/naming/variable_names.cr @@ -24,6 +24,7 @@ module Ameba::Rule::Naming # ``` class VariableNames < Base properties do + since_version "0.2.0" description "Enforces variable names to be in underscored case" end diff --git a/src/ameba/rule/performance/any_after_filter.cr b/src/ameba/rule/performance/any_after_filter.cr index c51f6b287..b916911cc 100644 --- a/src/ameba/rule/performance/any_after_filter.cr +++ b/src/ameba/rule/performance/any_after_filter.cr @@ -30,6 +30,7 @@ module Ameba::Rule::Performance include AST::Util properties do + since_version "0.8.1" description "Identifies usage of `any?` calls that follow filters" filter_names %w[select reject] end diff --git a/src/ameba/rule/performance/any_instead_of_empty.cr b/src/ameba/rule/performance/any_instead_of_empty.cr index 88ecc1066..5c2b9a81e 100644 --- a/src/ameba/rule/performance/any_instead_of_empty.cr +++ b/src/ameba/rule/performance/any_instead_of_empty.cr @@ -29,6 +29,7 @@ module Ameba::Rule::Performance # ``` class AnyInsteadOfEmpty < Base properties do + since_version "0.14.0" description "Identifies usage of arg-less `any?` calls" end diff --git a/src/ameba/rule/performance/chained_call_with_no_bang.cr b/src/ameba/rule/performance/chained_call_with_no_bang.cr index 52fd657da..bf4671568 100644 --- a/src/ameba/rule/performance/chained_call_with_no_bang.cr +++ b/src/ameba/rule/performance/chained_call_with_no_bang.cr @@ -40,6 +40,7 @@ module Ameba::Rule::Performance include AST::Util properties do + since_version "0.14.0" description "Identifies usage of chained calls not utilizing the bang method variants" # All of those have bang method variants returning `self` diff --git a/src/ameba/rule/performance/compact_after_map.cr b/src/ameba/rule/performance/compact_after_map.cr index 7ec44a1d8..2aff1af07 100644 --- a/src/ameba/rule/performance/compact_after_map.cr +++ b/src/ameba/rule/performance/compact_after_map.cr @@ -25,6 +25,7 @@ module Ameba::Rule::Performance include AST::Util properties do + since_version "0.14.0" description "Identifies usage of `compact` calls that follow `map`" end diff --git a/src/ameba/rule/performance/first_last_after_filter.cr b/src/ameba/rule/performance/first_last_after_filter.cr index 4290ba4ce..c260bb55e 100644 --- a/src/ameba/rule/performance/first_last_after_filter.cr +++ b/src/ameba/rule/performance/first_last_after_filter.cr @@ -29,6 +29,7 @@ module Ameba::Rule::Performance include AST::Util properties do + since_version "0.8.1" description "Identifies usage of `first/last/first?/last?` calls that follow filters" filter_names %w[select] end diff --git a/src/ameba/rule/performance/flatten_after_map.cr b/src/ameba/rule/performance/flatten_after_map.cr index cbdd4f1f9..98be51d61 100644 --- a/src/ameba/rule/performance/flatten_after_map.cr +++ b/src/ameba/rule/performance/flatten_after_map.cr @@ -25,6 +25,7 @@ module Ameba::Rule::Performance include AST::Util properties do + since_version "0.14.0" description "Identifies usage of `flatten` calls that follow `map`" end diff --git a/src/ameba/rule/performance/map_instead_of_block.cr b/src/ameba/rule/performance/map_instead_of_block.cr index cb4f6fd64..faa84d5ae 100644 --- a/src/ameba/rule/performance/map_instead_of_block.cr +++ b/src/ameba/rule/performance/map_instead_of_block.cr @@ -26,6 +26,7 @@ module Ameba::Rule::Performance include AST::Util properties do + since_version "0.14.0" description "Identifies usage of `sum/product` calls that follow `map`" end diff --git a/src/ameba/rule/performance/size_after_filter.cr b/src/ameba/rule/performance/size_after_filter.cr index 254576de1..beaa6680a 100644 --- a/src/ameba/rule/performance/size_after_filter.cr +++ b/src/ameba/rule/performance/size_after_filter.cr @@ -36,6 +36,7 @@ module Ameba::Rule::Performance include AST::Util properties do + since_version "0.8.1" description "Identifies usage of `size` calls that follow filter" filter_names %w[select reject] end diff --git a/src/ameba/rule/style/guard_clause.cr b/src/ameba/rule/style/guard_clause.cr index 348fd85c5..936251ce2 100644 --- a/src/ameba/rule/style/guard_clause.cr +++ b/src/ameba/rule/style/guard_clause.cr @@ -55,6 +55,7 @@ module Ameba::Rule::Style include AST::Util properties do + since_version "1.0.0" enabled false description "Check for conditionals that can be replaced with guard clauses" end diff --git a/src/ameba/rule/style/is_a_filter.cr b/src/ameba/rule/style/is_a_filter.cr index 1a252fc0c..3bcbbcaa4 100644 --- a/src/ameba/rule/style/is_a_filter.cr +++ b/src/ameba/rule/style/is_a_filter.cr @@ -42,6 +42,7 @@ module Ameba::Rule::Style include AST::Util properties do + since_version "0.14.0" description "Identifies usage of `is_a?/nil?` calls within filters" filter_names %w[select reject any? all? none? one?] end diff --git a/src/ameba/rule/style/is_a_nil.cr b/src/ameba/rule/style/is_a_nil.cr index 06681261d..ee0836817 100644 --- a/src/ameba/rule/style/is_a_nil.cr +++ b/src/ameba/rule/style/is_a_nil.cr @@ -23,6 +23,7 @@ module Ameba::Rule::Style include AST::Util properties do + since_version "0.13.0" description "Disallows calls to `is_a?(Nil)` in favor of `nil?`" end diff --git a/src/ameba/rule/style/large_numbers.cr b/src/ameba/rule/style/large_numbers.cr index b439ba612..db51857e4 100644 --- a/src/ameba/rule/style/large_numbers.cr +++ b/src/ameba/rule/style/large_numbers.cr @@ -28,6 +28,7 @@ module Ameba::Rule::Style # ``` class LargeNumbers < Base properties do + since_version "0.2.0" enabled false description "Disallows usage of large numbers without underscore" int_min_digits 6 diff --git a/src/ameba/rule/style/negated_conditions_in_unless.cr b/src/ameba/rule/style/negated_conditions_in_unless.cr index 129ba2740..c7b633706 100644 --- a/src/ameba/rule/style/negated_conditions_in_unless.cr +++ b/src/ameba/rule/style/negated_conditions_in_unless.cr @@ -28,6 +28,7 @@ module Ameba::Rule::Style # ``` class NegatedConditionsInUnless < Base properties do + since_version "0.2.0" description "Disallows negated conditions in unless" end diff --git a/src/ameba/rule/style/redundant_begin.cr b/src/ameba/rule/style/redundant_begin.cr index be21d73db..6ababbadb 100644 --- a/src/ameba/rule/style/redundant_begin.cr +++ b/src/ameba/rule/style/redundant_begin.cr @@ -59,6 +59,7 @@ module Ameba::Rule::Style include AST::Util properties do + since_version "0.3.0" description "Disallows redundant begin blocks" end diff --git a/src/ameba/rule/style/redundant_next.cr b/src/ameba/rule/style/redundant_next.cr index ce81d080f..63544c3d2 100644 --- a/src/ameba/rule/style/redundant_next.cr +++ b/src/ameba/rule/style/redundant_next.cr @@ -100,6 +100,7 @@ module Ameba::Rule::Style include AST::Util properties do + since_version "0.12.0" description "Reports redundant next expressions" allow_multi_next true diff --git a/src/ameba/rule/style/redundant_return.cr b/src/ameba/rule/style/redundant_return.cr index 234fd097e..ecfdfb6c5 100644 --- a/src/ameba/rule/style/redundant_return.cr +++ b/src/ameba/rule/style/redundant_return.cr @@ -97,6 +97,7 @@ module Ameba::Rule::Style include AST::Util properties do + since_version "0.9.0" description "Reports redundant return expressions" allow_multi_return true diff --git a/src/ameba/rule/style/unless_else.cr b/src/ameba/rule/style/unless_else.cr index ba9c380b6..97d677030 100644 --- a/src/ameba/rule/style/unless_else.cr +++ b/src/ameba/rule/style/unless_else.cr @@ -44,6 +44,7 @@ module Ameba::Rule::Style # ``` class UnlessElse < Base properties do + since_version "0.1.0" description "Disallows the use of an `else` block with the `unless`" end diff --git a/src/ameba/rule/style/verbose_block.cr b/src/ameba/rule/style/verbose_block.cr index fa4ae961e..df81d5532 100644 --- a/src/ameba/rule/style/verbose_block.cr +++ b/src/ameba/rule/style/verbose_block.cr @@ -31,6 +31,7 @@ module Ameba::Rule::Style include AST::Util properties do + since_version "0.14.0" description "Identifies usage of collapsible single expression blocks" exclude_multiple_line_blocks true diff --git a/src/ameba/rule/style/while_true.cr b/src/ameba/rule/style/while_true.cr index 700364027..60a8359f9 100644 --- a/src/ameba/rule/style/while_true.cr +++ b/src/ameba/rule/style/while_true.cr @@ -27,6 +27,7 @@ module Ameba::Rule::Style # ``` class WhileTrue < Base properties do + since_version "0.3.0" description "Disallows while statements with a true literal as condition" end From 42be5c66381d112146b4acc480f087bb7f44ba79 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Wed, 9 Oct 2024 02:18:06 +0200 Subject: [PATCH 12/16] Typo: `array` -> `arrays` --- src/ameba/rule/lint/{percent_array.cr => percent_arrays.cr} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/ameba/rule/lint/{percent_array.cr => percent_arrays.cr} (100%) diff --git a/src/ameba/rule/lint/percent_array.cr b/src/ameba/rule/lint/percent_arrays.cr similarity index 100% rename from src/ameba/rule/lint/percent_array.cr rename to src/ameba/rule/lint/percent_arrays.cr From f0fcc699a2dec9788d650e17cedd767ec79b403b Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Thu, 10 Oct 2024 01:41:03 +0200 Subject: [PATCH 13/16] Make `RuleVersionsPresenter` list all rules grouped by version --- .../presenter/rule_versions_presenter.cr | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/ameba/presenter/rule_versions_presenter.cr b/src/ameba/presenter/rule_versions_presenter.cr index 4c821f542..fdaf43321 100644 --- a/src/ameba/presenter/rule_versions_presenter.cr +++ b/src/ameba/presenter/rule_versions_presenter.cr @@ -1,11 +1,31 @@ module Ameba::Presenter class RuleVersionsPresenter < BasePresenter - def run(rules) - versions = - rules.compact_map(&.since_version).sort!.uniq! + def run(rules, verbose = true) + missing_version = SemanticVersion.new(0, 0, 0) + versions = rules + .sort_by { |rule| rule.since_version || missing_version } + .group_by(&.since_version) - versions.each do |version| - output.puts "- %s" % version.to_s.colorize(:green) + first = true + + versions.each do |version, version_rules| + if verbose + output.puts unless first + if version + output.puts "- %s" % version.to_s.colorize(:green) + else + output.puts "- %s" % "N/A".colorize(:dark_gray) + end + version_rules.map(&.name).sort!.each do |name| + output.puts " - %s" % name.colorize(:dark_gray) + end + else + if version + output.puts "- %s" % version.to_s.colorize(:green) + end + end + + first = false end end end From 549b0b8a9d4486f0d7aff282234ebafcf2b7d636 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Thu, 10 Oct 2024 02:30:51 +0200 Subject: [PATCH 14/16] Add spec for `RuleVersionsPresenter` --- .../presenter/rule_versions_presenter_spec.cr | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 spec/ameba/presenter/rule_versions_presenter_spec.cr diff --git a/spec/ameba/presenter/rule_versions_presenter_spec.cr b/spec/ameba/presenter/rule_versions_presenter_spec.cr new file mode 100644 index 000000000..282a45497 --- /dev/null +++ b/spec/ameba/presenter/rule_versions_presenter_spec.cr @@ -0,0 +1,33 @@ +require "../../spec_helper" + +module Ameba + private def with_rule_versions_presenter(&) + with_presenter(Presenter::RuleVersionsPresenter) do |presenter, io| + rules = Config.load.rules + presenter.run(rules) + + output = io.to_s + output = Formatter::Util.deansify(output).to_s + + yield rules, output, presenter + end + end + + describe Presenter::RuleVersionsPresenter do + it "outputs rule versions" do + with_rule_versions_presenter do |_rules, output| + output.should contain <<-TEXT + - 0.1.0 + - Layout/LineLength + - Layout/TrailingBlankLines + - Layout/TrailingWhitespace + - Lint/ComparisonToBoolean + - Lint/DebuggerStatement + - Lint/LiteralInCondition + - Lint/LiteralInInterpolation + - Style/UnlessElse + TEXT + end + end + end +end From 6665b51251f923f68e2983efb2762124c2bb417c Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Fri, 11 Oct 2024 22:12:17 +0200 Subject: [PATCH 15/16] Cleanup `with_presenter` helper method implementation --- spec/ameba/presenter/rule_collection_presenter_spec.cr | 8 ++------ spec/ameba/presenter/rule_presenter_spec.cr | 10 +++------- spec/ameba/presenter/rule_versions_presenter_spec.cr | 8 ++------ spec/spec_helper.cr | 9 +++++++-- 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/spec/ameba/presenter/rule_collection_presenter_spec.cr b/spec/ameba/presenter/rule_collection_presenter_spec.cr index cba4129b3..fbbd60577 100644 --- a/spec/ameba/presenter/rule_collection_presenter_spec.cr +++ b/spec/ameba/presenter/rule_collection_presenter_spec.cr @@ -2,13 +2,9 @@ require "../../spec_helper" module Ameba private def with_rule_collection_presenter(&) - with_presenter(Presenter::RuleCollectionPresenter) do |presenter, io| - rules = Config.load.rules - presenter.run(rules) - - output = io.to_s - output = Formatter::Util.deansify(output).to_s + rules = Config.load.rules + with_presenter(Presenter::RuleCollectionPresenter, rules) do |presenter, output| yield rules, output, presenter end end diff --git a/spec/ameba/presenter/rule_presenter_spec.cr b/spec/ameba/presenter/rule_presenter_spec.cr index 5f76007ac..c763dd5d1 100644 --- a/spec/ameba/presenter/rule_presenter_spec.cr +++ b/spec/ameba/presenter/rule_presenter_spec.cr @@ -2,14 +2,10 @@ require "../../spec_helper" module Ameba private def rule_presenter_each_rule(&) - with_presenter(Presenter::RulePresenter) do |presenter, io| - rules = Config.load.rules - rules.each do |rule| - presenter.run(rule) - - output = io.to_s - output = Formatter::Util.deansify(output).to_s + rules = Config.load.rules + rules.each do |rule| + with_presenter(Presenter::RulePresenter, rule) do |presenter, output| yield rule, output, presenter end end diff --git a/spec/ameba/presenter/rule_versions_presenter_spec.cr b/spec/ameba/presenter/rule_versions_presenter_spec.cr index 282a45497..3fd91a5f8 100644 --- a/spec/ameba/presenter/rule_versions_presenter_spec.cr +++ b/spec/ameba/presenter/rule_versions_presenter_spec.cr @@ -2,13 +2,9 @@ require "../../spec_helper" module Ameba private def with_rule_versions_presenter(&) - with_presenter(Presenter::RuleVersionsPresenter) do |presenter, io| - rules = Config.load.rules - presenter.run(rules) - - output = io.to_s - output = Formatter::Util.deansify(output).to_s + rules = Config.load.rules + with_presenter(Presenter::RuleVersionsPresenter, rules) do |presenter, output| yield rules, output, presenter end end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index acfcc3dac..e8de14918 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -294,11 +294,16 @@ module Ameba end end -def with_presenter(klass, &) +def with_presenter(klass, *args, deansify = true, **kwargs, &) io = IO::Memory.new + presenter = klass.new(io) + presenter.run(*args, **kwargs) + + output = io.to_s + output = Ameba::Formatter::Util.deansify(output).to_s if deansify - yield presenter, io + yield presenter, output end def as_node(source) From 3552fd0f611f0e0f97b5adf68d9d363820caa969 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Tue, 29 Oct 2024 02:03:49 +0100 Subject: [PATCH 16/16] Refactor `Runner#rule_runnable?` impl a bit more --- src/ameba/runner.cr | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ameba/runner.cr b/src/ameba/runner.cr index cc7aa66dc..5166de535 100644 --- a/src/ameba/runner.cr +++ b/src/ameba/runner.cr @@ -74,15 +74,18 @@ module Ameba protected def initialize(rules, @sources, @formatter, @severity, @autocorrect = false, @version = nil) @rules = - rules.select { |rule| rule_runnable?(rule) } + rules.select { |rule| rule_runnable?(rule, @version) } @unneeded_disable_directive_rule = rules.find &.class.==(Rule::Lint::UnneededDisableDirective) end - protected def rule_runnable?(rule) - rule.enabled? && !rule.special? && - (!(version = @version) || !(since_version = rule.since_version) || - since_version <= version) + protected def rule_runnable?(rule, version) + rule.enabled? && !rule.special? && rule_satisfies_version?(rule, version) + end + + protected def rule_satisfies_version?(rule, version) + !version || !(since_version = rule.since_version) || + since_version <= version end # Performs the inspection. Iterates through all sources and test it using