2017-11-07 21:50:25 +00:00
|
|
|
module Ameba::Rule
|
2018-02-02 20:11:18 +00:00
|
|
|
# List of names of the special rules, which
|
|
|
|
# behave differently than usual rules.
|
2018-02-02 08:52:21 +00:00
|
|
|
SPECIAL = [
|
2018-06-16 11:50:59 +00:00
|
|
|
Lint::Syntax.rule_name,
|
|
|
|
Lint::UnneededDisableDirective.rule_name,
|
2018-02-02 08:52:21 +00:00
|
|
|
]
|
|
|
|
|
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)
|
2018-06-10 21:15:12 +00:00
|
|
|
# issue_for line, column, "Something wrong."
|
2017-11-15 18:49:09 +00:00
|
|
|
# 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
|
2018-06-10 21:15:12 +00:00
|
|
|
# that are tested by this rule, it should add an issue.
|
2017-11-15 18:49:09 +00:00
|
|
|
#
|
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
|
2018-06-10 21:15:12 +00:00
|
|
|
# that are tested by this rule, it should add an issue.
|
2017-10-30 20:00:01 +00:00
|
|
|
abstract def test(source : Source)
|
|
|
|
|
2018-05-03 15:57:47 +00:00
|
|
|
def test(source : Source, node : Crystal::ASTNode, *opts)
|
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
|
2018-02-02 20:11:18 +00:00
|
|
|
{{@type}}.rule_name
|
2017-11-22 06:44:29 +00:00
|
|
|
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|
|
2018-05-29 10:19:00 +00:00
|
|
|
source.matches_path?(path) ||
|
|
|
|
Dir.glob(path).any? { |glob| source.matches_path? glob }
|
2017-12-18 11:06:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-02-02 20:11:18 +00:00
|
|
|
# Returns true if this rule is special and behaves differently than
|
|
|
|
# usual rules.
|
|
|
|
#
|
|
|
|
# ```
|
|
|
|
# my_rule.special? # => true or false
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
def special?
|
|
|
|
SPECIAL.includes? name
|
|
|
|
end
|
|
|
|
|
2018-06-10 21:15:12 +00:00
|
|
|
macro issue_for(*args)
|
|
|
|
source.add_issue self, {{*args}}
|
|
|
|
end
|
|
|
|
|
2018-02-02 20:11:18 +00:00
|
|
|
protected def self.rule_name
|
2018-06-16 11:50:59 +00:00
|
|
|
name.gsub("Ameba::Rule::", "").gsub("::", "/")
|
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-02-02 20:11:18 +00:00
|
|
|
Base.subclasses
|
2017-11-07 21:50:25 +00:00
|
|
|
end
|
2017-10-30 20:00:01 +00:00
|
|
|
end
|