mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Ameba::Rule -> Ameba::Rule::Base
This commit is contained in:
parent
b6c0d3e8ad
commit
80e2ab4f55
39 changed files with 79 additions and 73 deletions
|
@ -65,11 +65,11 @@ Finished in 10.53 milliseconds
|
|||
|
||||
## Write a new Rule
|
||||
|
||||
Adding a new rule is as simple as inheriting from `Rule` struct and implementing
|
||||
Adding a new rule is as simple as inheriting from `Rule::Base` struct and implementing
|
||||
your logic to detect a problem:
|
||||
|
||||
```crystal
|
||||
struct DebuggerStatement < Rule
|
||||
struct DebuggerStatement < Rule::Base
|
||||
# This is a required method to be implemented by the rule.
|
||||
# Source will be passed here. If rule finds an issue in this source,
|
||||
# it reports an error:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require "../../spec/spec_helper"
|
||||
|
||||
module Ameba
|
||||
describe Rule do
|
||||
describe Rule::Base do
|
||||
describe "#catch" do
|
||||
it "accepts and returns source" do
|
||||
s = Source.new "", ""
|
||||
|
@ -14,7 +14,9 @@ module Ameba
|
|||
DummyRule.new.name.should eq "Ameba::DummyRule"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe Rule do
|
||||
describe ".rules" do
|
||||
it "returns a list of all defined rules" do
|
||||
Rule.rules.includes?(DummyRule).should be_true
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = ComparisonToBoolean.new
|
||||
|
||||
describe ComparisonToBoolean do
|
|
@ -1,17 +1,17 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba
|
||||
subject = Rules::ConstantNames.new
|
||||
subject = Rule::ConstantNames.new
|
||||
|
||||
private def it_reports_constant(code, expected)
|
||||
it "reports constant name #{expected}" do
|
||||
s = Source.new code
|
||||
Rules::ConstantNames.new.catch(s).should_not be_valid
|
||||
Rule::ConstantNames.new.catch(s).should_not be_valid
|
||||
s.errors.first.message.should contain expected
|
||||
end
|
||||
end
|
||||
|
||||
describe Rules::ConstantNames do
|
||||
describe Rule::ConstantNames do
|
||||
it "passes if type names are screaming-cased" do
|
||||
s = Source.new %(
|
||||
LUCKY_NUMBERS = [3, 7, 11]
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = DebuggerStatement.new
|
||||
|
||||
describe DebuggerStatement do
|
|
@ -1,17 +1,17 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba
|
||||
subject = Rules::EmptyExpression.new
|
||||
subject = Rule::EmptyExpression.new
|
||||
|
||||
def it_detects_empty_expression(code)
|
||||
it "detects empty expression" do
|
||||
s = Source.new code
|
||||
rule = Rules::EmptyExpression.new
|
||||
rule = Rule::EmptyExpression.new
|
||||
rule.catch(s).should_not be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe Rules::EmptyExpression do
|
||||
describe Rule::EmptyExpression do
|
||||
it "passes if there is no empty expression" do
|
||||
s = Source.new %(
|
||||
def method()
|
|
@ -1,17 +1,17 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba
|
||||
subject = Rules::LargeNumbers.new
|
||||
subject = Rule::LargeNumbers.new
|
||||
|
||||
private def it_transforms(number, expected)
|
||||
it "transforms large number #{number}" do
|
||||
s = Source.new number
|
||||
Rules::LargeNumbers.new.catch(s).should_not be_valid
|
||||
Rule::LargeNumbers.new.catch(s).should_not be_valid
|
||||
s.errors.first.message.should contain expected
|
||||
end
|
||||
end
|
||||
|
||||
describe Rules::LargeNumbers do
|
||||
describe Rule::LargeNumbers do
|
||||
it "passes if large number does not require underscore" do
|
||||
s = Source.new %q(
|
||||
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = LineLength.new
|
||||
long_line = "*" * 81
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = LiteralInCondition.new
|
||||
|
||||
describe LiteralInCondition do
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = LiteralInInterpolation.new
|
||||
|
||||
describe LiteralInInterpolation do
|
|
@ -1,17 +1,17 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba
|
||||
subject = Rules::MethodNames.new
|
||||
subject = Rule::MethodNames.new
|
||||
|
||||
private def it_reports_method_name(code, expected)
|
||||
it "reports method name #{expected}" do
|
||||
s = Source.new code
|
||||
Rules::MethodNames.new.catch(s).should_not be_valid
|
||||
Rule::MethodNames.new.catch(s).should_not be_valid
|
||||
s.errors.first.message.should contain expected
|
||||
end
|
||||
end
|
||||
|
||||
describe Rules::MethodNames do
|
||||
describe Rule::MethodNames do
|
||||
it "passes if method names are underscore-cased" do
|
||||
s = Source.new %(
|
||||
class Person
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = NegatedConditionsInUnless.new
|
||||
|
||||
describe NegatedConditionsInUnless do
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = PredicateName.new
|
||||
|
||||
describe PredicateName do
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = TrailingBlankLines.new
|
||||
|
||||
describe TrailingBlankLines do
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = TrailingWhitespace.new
|
||||
|
||||
describe TrailingWhitespace do
|
|
@ -1,17 +1,17 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba
|
||||
subject = Rules::TypeNames.new
|
||||
subject = Rule::TypeNames.new
|
||||
|
||||
private def it_reports_name(code, expected)
|
||||
it "reports type name #{expected}" do
|
||||
s = Source.new code
|
||||
Rules::TypeNames.new.catch(s).should_not be_valid
|
||||
Rule::TypeNames.new.catch(s).should_not be_valid
|
||||
s.errors.first.message.should contain expected
|
||||
end
|
||||
end
|
||||
|
||||
describe Rules::TypeNames do
|
||||
describe Rule::TypeNames do
|
||||
it "passes if type names are camelcased" do
|
||||
s = Source.new %(
|
||||
class ParseError < Exception
|
|
@ -1,6 +1,6 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
subject = UnlessElse.new
|
||||
|
||||
describe UnlessElse do
|
|
@ -1,17 +1,17 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
module Ameba
|
||||
subject = Rules::VariableNames.new
|
||||
subject = Rule::VariableNames.new
|
||||
|
||||
private def it_reports_var_name(code, expected)
|
||||
it "reports method name #{expected}" do
|
||||
s = Source.new code
|
||||
Rules::VariableNames.new.catch(s).should_not be_valid
|
||||
Rule::VariableNames.new.catch(s).should_not be_valid
|
||||
s.errors.first.message.should contain expected
|
||||
end
|
||||
end
|
||||
|
||||
describe Rules::VariableNames do
|
||||
describe Rule::VariableNames do
|
||||
it "passes if var names are underscore-cased" do
|
||||
s = Source.new %(
|
||||
class Greeting
|
|
@ -2,7 +2,7 @@ require "spec"
|
|||
require "../src/ameba"
|
||||
|
||||
module Ameba
|
||||
struct DummyRule < Ameba::Rule
|
||||
struct DummyRule < Rule::Base
|
||||
def test(source)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
require "./ameba/*"
|
||||
require "./ameba/ast/*"
|
||||
require "./ameba/rules/*"
|
||||
require "./ameba/rule/*"
|
||||
require "./ameba/formatter/*"
|
||||
|
||||
module Ameba
|
||||
|
|
|
@ -21,7 +21,7 @@ module Ameba::AST
|
|||
]
|
||||
|
||||
class Visitor < Crystal::Visitor
|
||||
@rule : Rule
|
||||
@rule : Rule::Base
|
||||
@source : Source
|
||||
|
||||
def initialize(@rule, @source)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module Ameba
|
||||
abstract struct Rule
|
||||
module Ameba::Rule
|
||||
abstract struct Base
|
||||
abstract def test(source : Source)
|
||||
|
||||
def test(source : Source, node : Crystal::ASTNode)
|
||||
|
@ -11,11 +11,15 @@ module Ameba
|
|||
end
|
||||
|
||||
def name
|
||||
self.class.name.gsub("Ameba::Rules::", "")
|
||||
self.class.name.gsub("Ameba::Rule::", "")
|
||||
end
|
||||
|
||||
def self.rules
|
||||
protected def self.subclasses
|
||||
{{ @type.subclasses }}
|
||||
end
|
||||
end
|
||||
|
||||
def self.rules
|
||||
Base.subclasses
|
||||
end
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows comparison to booleans.
|
||||
#
|
||||
# For example, these are considered invalid:
|
||||
|
@ -12,7 +12,7 @@ module Ameba::Rules
|
|||
# could get the same result by using either the variable directly,
|
||||
# or negating the variable.
|
||||
#
|
||||
struct ComparisonToBoolean < Rule
|
||||
struct ComparisonToBoolean < Base
|
||||
def test(source)
|
||||
AST::Visitor.new self, source
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that enforces constant names to be in screaming case.
|
||||
#
|
||||
# For example, these constant names are considered valid:
|
||||
|
@ -15,7 +15,7 @@ module Ameba::Rules
|
|||
# Wrong_NAME = 2
|
||||
# ```
|
||||
#
|
||||
struct ConstantNames < Rule
|
||||
struct ConstantNames < Base
|
||||
def test(source)
|
||||
AST::Visitor.new self, source
|
||||
end
|
|
@ -1,10 +1,10 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows calls to debugger.
|
||||
#
|
||||
# This is because we don't want debugger breakpoints accidentally being
|
||||
# committed into our codebase.
|
||||
#
|
||||
struct DebuggerStatement < Rule
|
||||
struct DebuggerStatement < Base
|
||||
def test(source)
|
||||
AST::Visitor.new self, source
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows empty expressions.
|
||||
#
|
||||
# This is considered invalid:
|
||||
|
@ -21,7 +21,7 @@ module Ameba::Rules
|
|||
# end
|
||||
# ```
|
||||
#
|
||||
struct EmptyExpression < Rule
|
||||
struct EmptyExpression < Base
|
||||
include AST::Util
|
||||
|
||||
def test(source)
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows usage of large numbers without underscore.
|
||||
# These do not affect the value of the number, but can help read
|
||||
# large numbers more easily.
|
||||
|
@ -19,7 +19,7 @@ module Ameba::Rules
|
|||
# 5.123_45
|
||||
# ```
|
||||
#
|
||||
struct LargeNumbers < Rule
|
||||
struct LargeNumbers < Base
|
||||
def test(source)
|
||||
Tokenizer.new(source).run do |token|
|
||||
next unless token.type == :NUMBER && decimal?(token.raw)
|
|
@ -1,7 +1,7 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows lines longer than 80 symbols.
|
||||
#
|
||||
struct LineLength < Rule
|
||||
struct LineLength < Base
|
||||
def test(source)
|
||||
source.lines.each_with_index do |line, index|
|
||||
next unless line.size > 80
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows useless conditional statements that contain a literal
|
||||
# in place of a variable or predicate function.
|
||||
#
|
||||
|
@ -13,7 +13,7 @@ module Ameba::Rules
|
|||
# end
|
||||
# ```
|
||||
#
|
||||
struct LiteralInCondition < Rule
|
||||
struct LiteralInCondition < Base
|
||||
include AST::Util
|
||||
|
||||
def test(source)
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows useless string interpolations
|
||||
# that contain a literal value instead of a variable or function.
|
||||
#
|
||||
|
@ -9,7 +9,7 @@ module Ameba::Rules
|
|||
# "There are #{4} cats"
|
||||
# ```
|
||||
#
|
||||
struct LiteralInInterpolation < Rule
|
||||
struct LiteralInInterpolation < Base
|
||||
include AST::Util
|
||||
|
||||
def test(source)
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that enforces method names to be in underscored case.
|
||||
#
|
||||
# For example, these are considered valid:
|
||||
|
@ -30,7 +30,7 @@ module Ameba::Rules
|
|||
# end
|
||||
# end
|
||||
# ```
|
||||
struct MethodNames < Rule
|
||||
struct MethodNames < Base
|
||||
def test(source)
|
||||
AST::Visitor.new self, source
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows negated conditions in unless.
|
||||
#
|
||||
# For example, this is considered invalid:
|
||||
|
@ -20,7 +20,7 @@ module Ameba::Rules
|
|||
# It is pretty difficult to wrap your head around a block of code
|
||||
# that is executed if a negated condition is NOT met.
|
||||
#
|
||||
struct NegatedConditionsInUnless < Rule
|
||||
struct NegatedConditionsInUnless < Base
|
||||
def test(source)
|
||||
AST::Visitor.new self, source
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows tautological predicate names, meaning those that
|
||||
# start with the prefix `has_` or the prefix `is_`.
|
||||
#
|
||||
|
@ -22,7 +22,7 @@ module Ameba::Rules
|
|||
# end
|
||||
# ```
|
||||
#
|
||||
struct PredicateName < Rule
|
||||
struct PredicateName < Base
|
||||
def test(source)
|
||||
AST::Visitor.new self, source
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows trailing blank lines at the end of the source file.
|
||||
#
|
||||
struct TrailingBlankLines < Rule
|
||||
struct TrailingBlankLines < Base
|
||||
def test(source)
|
||||
if source.lines.size > 1 && source.lines[-2, 2].join.strip.empty?
|
||||
source.error self, source.location(source.lines.size, 1),
|
|
@ -1,7 +1,7 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows trailing whitespaces.
|
||||
#
|
||||
struct TrailingWhitespace < Rule
|
||||
struct TrailingWhitespace < Base
|
||||
def test(source)
|
||||
source.lines.each_with_index do |line, index|
|
||||
next unless line =~ /\s$/
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that enforces type names in camelcase manner.
|
||||
#
|
||||
# For example, these are considered valid:
|
||||
|
@ -45,7 +45,7 @@ module Ameba::Rules
|
|||
# end
|
||||
# ```
|
||||
#
|
||||
struct TypeNames < Rule
|
||||
struct TypeNames < Base
|
||||
def test(source)
|
||||
AST::Visitor.new self, source
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that disallows the use of an `else` block with the `unless`.
|
||||
#
|
||||
# For example, the rule considers these valid:
|
||||
|
@ -36,7 +36,7 @@ module Ameba::Rules
|
|||
# end
|
||||
# ```
|
||||
#
|
||||
struct UnlessElse < Rule
|
||||
struct UnlessElse < Base
|
||||
def test(source)
|
||||
AST::Visitor.new self, source
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
module Ameba::Rules
|
||||
module Ameba::Rule
|
||||
# A rule that enforces variable names to be in underscored case.
|
||||
#
|
||||
# For example, these variable names are considered valid:
|
||||
|
@ -24,7 +24,7 @@ module Ameba::Rules
|
|||
# wrong_Name = 2
|
||||
# ```
|
||||
#
|
||||
struct VariableNames < Rule
|
||||
struct VariableNames < Base
|
||||
def test(source)
|
||||
AST::Visitor.new self, source
|
||||
end
|
|
@ -5,9 +5,9 @@ module Ameba
|
|||
# Represents an error caught by Ameba.
|
||||
#
|
||||
# Each error has the rule that created this error,
|
||||
# position of the error and a message.
|
||||
# location of the issue and a message.
|
||||
record Error,
|
||||
rule : Rule,
|
||||
rule : Rule::Base,
|
||||
location : Crystal::Location?,
|
||||
message : String
|
||||
|
||||
|
@ -20,7 +20,7 @@ module Ameba
|
|||
def initialize(@code : String, @path = nil)
|
||||
end
|
||||
|
||||
def error(rule : Rule, location, message : String)
|
||||
def error(rule : Rule::Base, location, message : String)
|
||||
errors << Error.new rule, location, message
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue