mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Make config loading more flexible
This commit is contained in:
parent
b5c9f4dff6
commit
f4f401d56f
33 changed files with 243 additions and 92 deletions
31
README.md
31
README.md
|
@ -108,35 +108,32 @@ Each rule is enabled by default, even if you remove it from the config file.
|
||||||
|
|
||||||
## Writing a new Rule
|
## Writing a new Rule
|
||||||
|
|
||||||
Adding a new rule is as simple as inheriting from `Rule::Base` struct and implementing
|
Adding a new rule is as simple as inheriting from `Ameba::Rule::Base` struct and implementing
|
||||||
your logic to detect a problem:
|
a logic to detect a problem in the source file:
|
||||||
|
|
||||||
```crystal
|
```crystal
|
||||||
struct DebuggerStatement < Rule::Base
|
struct MySuperRule < Ameba::Rule::Base
|
||||||
# This is a required method to be implemented by the rule.
|
# This is a required method to be implemented by the rule.
|
||||||
# Source will be passed here. If rule finds an issue in this source,
|
# Source will be passed here. If rule detects an issue in the source,
|
||||||
# it reports an error:
|
# it reports an error:
|
||||||
#
|
#
|
||||||
# source.error rule, line_number, message
|
# source.error rule, location, message
|
||||||
#
|
#
|
||||||
def test(source)
|
def test(source)
|
||||||
# This line deletegates verification to a particular callback in the AST visitor.
|
# TODO: test source
|
||||||
AST::Visitor.new self, source
|
|
||||||
end
|
|
||||||
|
|
||||||
# This method is called once the visitor finds a needed node.
|
|
||||||
def test(source, node : Crystal::Call)
|
|
||||||
# It reports an error, if there is `debugger` method call
|
|
||||||
# without arguments and a receiver. That's it, somebody forgot
|
|
||||||
# to remove a debugger statement.
|
|
||||||
return unless node.name == "debugger" && node.args.empty? && node.obj.nil?
|
|
||||||
|
|
||||||
source.error self, node.location, "Possible forgotten debugger statement detected"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
As soon as a custom rule is defined, it becomes available in a full set of rules
|
||||||
|
executed by default and also can be configured via config file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
MySuperRule:
|
||||||
|
Enabled: false
|
||||||
|
```
|
||||||
|
|
||||||
## Credits & inspirations
|
## Credits & inspirations
|
||||||
|
|
||||||
- [Crystal Language](crystal-lang.org)
|
- [Crystal Language](crystal-lang.org)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
ComparisonToBoolean:
|
ComparisonToBoolean:
|
||||||
# Disallows comparison to booleans in conditions.
|
# Disallows comparison to booleans in conditions.
|
||||||
Enabled: true
|
Enabled: false
|
||||||
|
|
||||||
ConstantNames:
|
ConstantNames:
|
||||||
# Enforces constant names to be in a screaming case.
|
# Enforces constant names to be in a screaming case.
|
||||||
|
@ -33,7 +33,7 @@ LargeNumbers:
|
||||||
|
|
||||||
LineLength:
|
LineLength:
|
||||||
# Disallows lines longer that MaxLength number of symbols.
|
# Disallows lines longer that MaxLength number of symbols.
|
||||||
Enabled: true
|
Enabled: false
|
||||||
MaxLength: 80
|
MaxLength: 80
|
||||||
|
|
||||||
LiteralInCondition:
|
LiteralInCondition:
|
||||||
|
|
28
spec/ameba/base_spec.cr
Normal file
28
spec/ameba/base_spec.cr
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
require "../spec_helper"
|
||||||
|
|
||||||
|
module Ameba::Rule
|
||||||
|
struct NoProperties < Rule::Base
|
||||||
|
def test(source)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Base do
|
||||||
|
context "properties" do
|
||||||
|
subject = DummyRule.new
|
||||||
|
|
||||||
|
it "is enabled by default" do
|
||||||
|
subject.enabled.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "has a description property" do
|
||||||
|
subject.description.should_not be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "when a rule does not have defined properties" do
|
||||||
|
it "is enabled by default" do
|
||||||
|
NoProperties.new.enabled.should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -112,7 +112,7 @@ module Ameba::Rule
|
||||||
error = s.errors.first
|
error = s.errors.first
|
||||||
error.rule.should_not be_nil
|
error.rule.should_not be_nil
|
||||||
error.location.to_s.should eq "source.cr:2:9"
|
error.location.to_s.should eq "source.cr:2:9"
|
||||||
error.message.should eq "Duplicated when conditions in case."
|
error.message.should eq "Duplicated when conditions in case"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -116,5 +116,14 @@ module Ameba
|
||||||
error.location.to_s.should eq "source.cr:2:10"
|
error.location.to_s.should eq "source.cr:2:10"
|
||||||
error.message.should match /1_200_000/
|
error.message.should match /1_200_000/
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "properties" do
|
||||||
|
it "allows to configure integer min digits" do
|
||||||
|
s = Source.new %q(1200000)
|
||||||
|
rule = Rule::LargeNumbers.new
|
||||||
|
rule.int_min_digits = 10
|
||||||
|
rule.catch(s).should be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,5 +29,14 @@ module Ameba::Rule
|
||||||
error.location.to_s.should eq "source.cr:1:81"
|
error.location.to_s.should eq "source.cr:1:81"
|
||||||
error.message.should eq "Line too long"
|
error.message.should eq "Line too long"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "properties" do
|
||||||
|
it "allows to configure max length of the line" do
|
||||||
|
source = Source.new long_line
|
||||||
|
rule = LineLength.new
|
||||||
|
rule.max_length = long_line.size
|
||||||
|
rule.catch(source).should be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -65,5 +65,21 @@ module Ameba::Rule
|
||||||
"Symbols `,\"` may be unwanted in %w array literals"
|
"Symbols `,\"` may be unwanted in %w array literals"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "properties" do
|
||||||
|
it "allows to configure string_array_unwanted_symbols" do
|
||||||
|
rule = PercentArrays.new
|
||||||
|
rule.string_array_unwanted_symbols = ","
|
||||||
|
s = Source.new %( %w("one") )
|
||||||
|
rule.catch(s).should be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows to configure symbol_array_unwanted_symbols" do
|
||||||
|
rule = PercentArrays.new
|
||||||
|
rule.symbol_array_unwanted_symbols = ","
|
||||||
|
s = Source.new %( %i(:one) )
|
||||||
|
rule.catch(s).should be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -207,7 +207,7 @@ module Ameba::Rule
|
||||||
error = s.errors.first
|
error = s.errors.first
|
||||||
error.rule.should_not be_nil
|
error.rule.should_not be_nil
|
||||||
error.location.to_s.should eq "source.cr:2:9"
|
error.location.to_s.should eq "source.cr:2:9"
|
||||||
error.message.should eq "Redundant `begin` block detected."
|
error.message.should eq "Redundant `begin` block detected"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,10 @@ require "../src/ameba"
|
||||||
|
|
||||||
module Ameba
|
module Ameba
|
||||||
struct DummyRule < Rule::Base
|
struct DummyRule < Rule::Base
|
||||||
|
properties do
|
||||||
|
description : String = "Dummy rule that does nothing."
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -80,66 +80,71 @@ class Ameba::Config
|
||||||
Formatter::DotFormatter.new
|
Formatter::DotFormatter.new
|
||||||
end
|
end
|
||||||
|
|
||||||
# An entity that represents a corresponding configuration for a specific Rule.
|
# :no_doc:
|
||||||
module Rule
|
module Rule
|
||||||
# Represents a configuration of a specific Rule.
|
macro properties(&block)
|
||||||
getter config : YAML::Any?
|
{% definitions = [] of NamedTuple %}
|
||||||
|
{% if block.body.is_a? Assign %}
|
||||||
# A macro that defines a dsl to define configurable properties.
|
{% definitions << {var: block.body.target, value: block.body.value} %}
|
||||||
#
|
{% elsif block.body.is_a? TypeDeclaration %}
|
||||||
# ```
|
{% definitions << {var: block.body.var, value: block.body.value, type: block.body.type} %}
|
||||||
# class Configurable
|
{% elsif block.body.is_a? Expressions %}
|
||||||
# include Ameba::Config::Rule
|
{% for prop in block.body.expressions %}
|
||||||
#
|
{% if prop.is_a? Assign %}
|
||||||
# prop enabled? = false
|
{% definitions << {var: prop.target, value: prop.value} %}
|
||||||
# prop max_length = 80
|
{% elsif prop.is_a? TypeDeclaration %}
|
||||||
# prop wildcard = "*"
|
{% definitions << {var: prop.var, value: prop.value, type: prop.type} %}
|
||||||
# end
|
{% end %}
|
||||||
# ```
|
|
||||||
#
|
|
||||||
macro prop(assign)
|
|
||||||
# Rule configuration property.
|
|
||||||
def {{assign.target}}
|
|
||||||
{% prop_name = assign.target.id.camelcase.gsub(/\?/, "") %}
|
|
||||||
|
|
||||||
{% if assign.value.is_a? NumberLiteral %}
|
|
||||||
int_prop "{{prop_name}}", {{assign.value}}
|
|
||||||
{% elsif assign.value.is_a? BoolLiteral %}
|
|
||||||
bool_prop "{{prop_name}}", {{assign.value}}
|
|
||||||
{% elsif assign.value.is_a? StringLiteral %}
|
|
||||||
str_prop "{{prop_name}}", {{assign.value}}
|
|
||||||
{% end %}
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% properties = {} of MacroId => NamedTuple %}
|
||||||
|
{% for df in definitions %}
|
||||||
|
{% name = df[:var].id %}
|
||||||
|
{% key = name.camelcase.stringify %}
|
||||||
|
{% value = df[:value] %}
|
||||||
|
{% type = df[:type] %}
|
||||||
|
|
||||||
|
{% if type == nil %}
|
||||||
|
{% if value.is_a? BoolLiteral %}
|
||||||
|
{% type = Bool %}
|
||||||
|
{% elsif value.is_a? StringLiteral %}
|
||||||
|
{% type = String %}
|
||||||
|
{% elsif value.is_a? NumberLiteral %}
|
||||||
|
{% if value.kind == :i32 %}
|
||||||
|
{% type = Int32 %}
|
||||||
|
{% elsif value.kind == :i64 %}
|
||||||
|
{% type = Int64 %}
|
||||||
|
{% elsif value.kind == :f32 %}
|
||||||
|
{% type = Float32 %}
|
||||||
|
{% elsif value.kind == :f64 %}
|
||||||
|
{% type = Float64 %}
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% type = Nil if type == nil %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% properties[name] = {key: key, default: value, type: type} %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% if properties["enabled".id] == nil %}
|
||||||
|
{% properties["enabled".id] = {key: "Enabled", default: true, type: Bool} %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
YAML.mapping({{properties}})
|
||||||
|
end
|
||||||
|
|
||||||
|
macro included
|
||||||
|
macro inherited
|
||||||
|
# allow creating rules without properties
|
||||||
|
properties {}
|
||||||
|
|
||||||
|
def self.new(config : Ameba::Config? = nil)
|
||||||
|
yaml = config.try &.subconfig(class_name).try &.to_yaml || "{}"
|
||||||
|
from_yaml yaml
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates an instance of a Rule configuration.
|
|
||||||
#
|
|
||||||
# ```
|
|
||||||
# class Configurable
|
|
||||||
# include Ameba::Config::Rule
|
|
||||||
#
|
|
||||||
# prop enabled? = false
|
|
||||||
# prop max_length = 80
|
|
||||||
# prop wildcard = "*"
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Configurable.new config
|
|
||||||
# ```
|
|
||||||
#
|
|
||||||
def initialize(config = nil)
|
|
||||||
@config = config.try &.subconfig(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected def int_prop(name, default : Number)
|
|
||||||
str_prop(name, default).to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
protected def bool_prop(name, default : Bool)
|
|
||||||
str_prop(name, default.to_s) == "true"
|
|
||||||
end
|
|
||||||
|
|
||||||
protected def str_prop(name, default)
|
|
||||||
config.try &.[name]?.try &.as_s || default
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,10 +27,6 @@ module Ameba::Rule
|
||||||
# that are tested by this rule, it should add an error.
|
# that are tested by this rule, it should add an error.
|
||||||
abstract def test(source : Source)
|
abstract def test(source : Source)
|
||||||
|
|
||||||
# Enabled property indicates whether this rule enabled or not.
|
|
||||||
# Only enabled rules will be included into the inspection.
|
|
||||||
prop enabled? = true
|
|
||||||
|
|
||||||
def test(source : Source, node : Crystal::ASTNode)
|
def test(source : Source, node : Crystal::ASTNode)
|
||||||
# can't be abstract
|
# can't be abstract
|
||||||
end
|
end
|
||||||
|
@ -59,7 +55,11 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
def name
|
def name
|
||||||
self.class.name.gsub("Ameba::Rule::", "")
|
{{@type}}.class_name
|
||||||
|
end
|
||||||
|
|
||||||
|
protected def self.class_name
|
||||||
|
name.gsub("Ameba::Rule::", "")
|
||||||
end
|
end
|
||||||
|
|
||||||
protected def self.subclasses
|
protected def self.subclasses
|
||||||
|
|
|
@ -21,6 +21,11 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct ComparisonToBoolean < Base
|
struct ComparisonToBoolean < Base
|
||||||
|
properties do
|
||||||
|
enabled = false
|
||||||
|
description = "Disallows comparison to booleans"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,6 +23,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct ConstantNames < Base
|
struct ConstantNames < Base
|
||||||
|
properties do
|
||||||
|
description = "Enforces constant names to be in screaming case"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct DebuggerStatement < Base
|
struct DebuggerStatement < Base
|
||||||
|
properties do
|
||||||
|
description = "Disallows calls to debugger"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,6 +31,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct DuplicatedWhen < Base
|
struct DuplicatedWhen < Base
|
||||||
|
properties do
|
||||||
|
description = "Disallows duplicated when conditions in case"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
@ -38,7 +42,7 @@ module Ameba::Rule
|
||||||
def test(source, node : Crystal::Case)
|
def test(source, node : Crystal::Case)
|
||||||
return unless duplicated_whens?(node.whens)
|
return unless duplicated_whens?(node.whens)
|
||||||
|
|
||||||
source.error self, node.location, "Duplicated when conditions in case."
|
source.error self, node.location, "Duplicated when conditions in case"
|
||||||
end
|
end
|
||||||
|
|
||||||
private def duplicated_whens?(whens)
|
private def duplicated_whens?(whens)
|
||||||
|
|
|
@ -33,6 +33,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct EmptyEnsure < Base
|
struct EmptyEnsure < Base
|
||||||
|
properties do
|
||||||
|
description = "Disallows empty ensure statement"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,6 +31,10 @@ module Ameba::Rule
|
||||||
struct EmptyExpression < Base
|
struct EmptyExpression < Base
|
||||||
include AST::Util
|
include AST::Util
|
||||||
|
|
||||||
|
properties do
|
||||||
|
description = "Disallows empty expressions"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,6 +21,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct HashDuplicatedKey < Base
|
struct HashDuplicatedKey < Base
|
||||||
|
properties do
|
||||||
|
description = "Disallows duplicated keys in hash literals"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,7 +28,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct LargeNumbers < Base
|
struct LargeNumbers < Base
|
||||||
prop int_min_digits = 5
|
properties do
|
||||||
|
description = "Disallows usage of large numbers without underscore"
|
||||||
|
int_min_digits = 5
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
Tokenizer.new(source).run do |token|
|
Tokenizer.new(source).run do |token|
|
||||||
|
|
|
@ -10,7 +10,11 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct LineLength < Base
|
struct LineLength < Base
|
||||||
prop max_length = 80
|
properties do
|
||||||
|
enabled = false
|
||||||
|
description = "Disallows lines longer than `MaxLength` number of symbols"
|
||||||
|
max_length = 80
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
source.lines.each_with_index do |line, index|
|
source.lines.each_with_index do |line, index|
|
||||||
|
|
|
@ -23,6 +23,11 @@ module Ameba::Rule
|
||||||
struct LiteralInCondition < Base
|
struct LiteralInCondition < Base
|
||||||
include AST::Util
|
include AST::Util
|
||||||
|
|
||||||
|
properties do
|
||||||
|
description = "Disallows useless conditional statements that contain\
|
||||||
|
a literal in place of a variable or predicate function"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,6 +19,10 @@ module Ameba::Rule
|
||||||
struct LiteralInInterpolation < Base
|
struct LiteralInInterpolation < Base
|
||||||
include AST::Util
|
include AST::Util
|
||||||
|
|
||||||
|
properties do
|
||||||
|
description = "Disallows useless string interpolations"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,6 +39,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct MethodNames < Base
|
struct MethodNames < Base
|
||||||
|
properties do
|
||||||
|
description = "Enforces method names to be in underscored case"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,6 +28,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct NegatedConditionsInUnless < Base
|
struct NegatedConditionsInUnless < Base
|
||||||
|
properties do
|
||||||
|
description = "Disallows negated conditions in unless"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,8 +25,11 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct PercentArrays < Base
|
struct PercentArrays < Base
|
||||||
prop string_array_unwanted_symbols = ",\""
|
properties do
|
||||||
prop symbol_array_unwanted_symbols = ",:"
|
description = "Disallows some unwanted symbols in percent array literals"
|
||||||
|
string_array_unwanted_symbols = ",\""
|
||||||
|
symbol_array_unwanted_symbols = ",:"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
error = start_token = nil
|
error = start_token = nil
|
||||||
|
|
|
@ -30,6 +30,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct PredicateName < Base
|
struct PredicateName < Base
|
||||||
|
properties do
|
||||||
|
description = "Disallows tautological predicate names"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,6 +57,9 @@ module Ameba::Rule
|
||||||
#
|
#
|
||||||
struct RedundantBegin < Base
|
struct RedundantBegin < Base
|
||||||
include AST::Util
|
include AST::Util
|
||||||
|
properties do
|
||||||
|
description = "Disallows redundant begin blocks"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
|
@ -65,7 +68,7 @@ module Ameba::Rule
|
||||||
def test(source, node : Crystal::Def)
|
def test(source, node : Crystal::Def)
|
||||||
return unless redundant_begin?(source, node)
|
return unless redundant_begin?(source, node)
|
||||||
|
|
||||||
source.error self, node.location, "Redundant `begin` block detected."
|
source.error self, node.location, "Redundant `begin` block detected"
|
||||||
end
|
end
|
||||||
|
|
||||||
private def redundant_begin?(source, node)
|
private def redundant_begin?(source, node)
|
||||||
|
|
|
@ -9,6 +9,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct TrailingBlankLines < Base
|
struct TrailingBlankLines < Base
|
||||||
|
properties do
|
||||||
|
description = "Disallows trailing blank lines"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
if source.lines.size > 1 && source.lines[-2, 2].join.strip.empty?
|
if source.lines.size > 1 && source.lines[-2, 2].join.strip.empty?
|
||||||
source.error self, source.location(source.lines.size, 1),
|
source.error self, source.location(source.lines.size, 1),
|
||||||
|
|
|
@ -9,6 +9,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct TrailingWhitespace < Base
|
struct TrailingWhitespace < Base
|
||||||
|
properties do
|
||||||
|
description = "Disallows trailing whitespaces"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
source.lines.each_with_index do |line, index|
|
source.lines.each_with_index do |line, index|
|
||||||
next unless line =~ /\s$/
|
next unless line =~ /\s$/
|
||||||
|
|
|
@ -53,6 +53,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct TypeNames < Base
|
struct TypeNames < Base
|
||||||
|
properties do
|
||||||
|
description = "Enforces type names in camelcase manner"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,6 +44,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct UnlessElse < Base
|
struct UnlessElse < Base
|
||||||
|
properties do
|
||||||
|
description = "Disallows the use of an `else` block with the `unless`"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,6 +32,10 @@ module Ameba::Rule
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
struct VariableNames < Base
|
struct VariableNames < Base
|
||||||
|
properties do
|
||||||
|
description = "Enforces variable names to be in underscored case"
|
||||||
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
AST::Visitor.new self, source
|
AST::Visitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
|
@ -93,7 +93,7 @@ module Ameba
|
||||||
end
|
end
|
||||||
|
|
||||||
private def load_rules(config)
|
private def load_rules(config)
|
||||||
Rule.rules.map { |r| r.new config }.select &.enabled?
|
Rule.rules.map { |r| r.new config }.select &.enabled
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue