2017-11-07 21:50:25 +00:00
|
|
|
module Ameba::Rule
|
2018-02-02 08:52:21 +00:00
|
|
|
SPECIAL = [
|
|
|
|
Syntax.class_name,
|
|
|
|
UnneededDisableDirective.class_name,
|
|
|
|
]
|
|
|
|
|
2017-11-15 18:49:09 +00:00
|
|
|
# Represents a base of all rules. In other words, all rules
|
|
|
|
# inherits from this struct:
|
|
|
|
#
|
|
|
|
# ```
|
|
|
|
# struct MyRule < Ameba::Rule::Base
|
|
|
|
# def test(source)
|
|
|
|
# if invalid?(source)
|
|
|
|
# source.error self, location, "Something wrong."
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# private def invalid?(source)
|
|
|
|
# # ...
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# Enforces rules to implement an abstract `#test` method which
|
|
|
|
# is designed to test the source passed in. If source has issues
|
|
|
|
# that are tested by this rule, it should add an error.
|
|
|
|
#
|
2017-11-07 21:50:25 +00:00
|
|
|
abstract struct Base
|
2017-11-23 17:49:45 +00:00
|
|
|
include Config::RuleConfig
|
2017-11-13 21:20:22 +00:00
|
|
|
|
2017-11-15 18:49:09 +00:00
|
|
|
# This method is designed to test the source passed in. If source has issues
|
|
|
|
# that are tested by this rule, it should add an error.
|
2017-10-30 20:00:01 +00:00
|
|
|
abstract def test(source : Source)
|
|
|
|
|
2017-10-31 22:47:29 +00:00
|
|
|
def test(source : Source, node : Crystal::ASTNode)
|
2017-11-06 18:54:58 +00:00
|
|
|
# can't be abstract
|
2017-10-31 22:47:29 +00:00
|
|
|
end
|
|
|
|
|
2017-11-15 18:49:09 +00:00
|
|
|
# A convenient addition to `#test` method that does the same
|
|
|
|
# but returns a passed in `source` as an addition.
|
|
|
|
#
|
|
|
|
# ```
|
|
|
|
# source = MyRule.new.catch(source)
|
|
|
|
# source.valid?
|
|
|
|
# ```
|
|
|
|
#
|
2017-10-30 20:00:01 +00:00
|
|
|
def catch(source : Source)
|
|
|
|
source.tap { |s| test s }
|
|
|
|
end
|
|
|
|
|
2017-11-15 18:49:09 +00:00
|
|
|
# Returns a name of this rule, which is basically a class name.
|
|
|
|
#
|
|
|
|
# ```
|
|
|
|
# struct MyRule < Ameba::Rule::Base
|
|
|
|
# def test(source)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# MyRule.new.name # => "MyRule"
|
|
|
|
# ```
|
|
|
|
#
|
2017-10-30 20:00:01 +00:00
|
|
|
def name
|
2017-11-22 06:44:29 +00:00
|
|
|
{{@type}}.class_name
|
|
|
|
end
|
|
|
|
|
2017-12-18 11:06:19 +00:00
|
|
|
# Checks whether the source is excluded from this rule.
|
|
|
|
# It searches for a path in `excluded` property which matches
|
|
|
|
# the one of the given source.
|
|
|
|
#
|
|
|
|
# ```
|
|
|
|
# my_rule.excluded?(source) # => true or false
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
def excluded?(source)
|
|
|
|
excluded.try &.any? do |path|
|
|
|
|
# TODO: file pattern match
|
|
|
|
source.path == path || source.fullpath == File.expand_path(path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-11-22 06:44:29 +00:00
|
|
|
protected def self.class_name
|
|
|
|
name.gsub("Ameba::Rule::", "")
|
2017-10-30 20:00:01 +00:00
|
|
|
end
|
2017-11-01 10:49:03 +00:00
|
|
|
|
2017-11-07 21:50:25 +00:00
|
|
|
protected def self.subclasses
|
2017-11-01 10:49:03 +00:00
|
|
|
{{ @type.subclasses }}
|
|
|
|
end
|
2017-10-30 20:00:01 +00:00
|
|
|
end
|
2017-11-07 21:50:25 +00:00
|
|
|
|
2018-01-25 09:50:11 +00:00
|
|
|
# Returns a list of all available rules
|
|
|
|
# (except a `Rule::Syntax` which is a special rule).
|
2017-11-15 18:49:09 +00:00
|
|
|
#
|
|
|
|
# ```
|
|
|
|
# Ameba::Rule.rules # => [LineLength, ConstantNames, ....]
|
|
|
|
# ```
|
|
|
|
#
|
2017-11-07 21:50:25 +00:00
|
|
|
def self.rules
|
2018-01-25 09:50:11 +00:00
|
|
|
Base.subclasses.reject! &.== Rule::Syntax
|
2017-11-07 21:50:25 +00:00
|
|
|
end
|
2017-10-30 20:00:01 +00:00
|
|
|
end
|