Misc refactors (#180)

* Optimize Severity#symbol

* Remove empty else branches

* Optimize map+compact/flatten calls

* Misc stylistic refactors
This commit is contained in:
Sijawusz Pur Rahnama 2021-01-11 19:13:58 +01:00 committed by GitHub
parent 9d85e731f5
commit 19c370aee0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 87 additions and 98 deletions

View file

@ -37,8 +37,7 @@ module Ameba::AST
# Returns true if this node or one of the parent branchables is a loop, false otherwise. # Returns true if this node or one of the parent branchables is a loop, false otherwise.
def loop? def loop?
return true if loop?(node) loop?(node) || parent.try(&.loop?) || false
parent.try(&.loop?) || false
end end
end end
end end

View file

@ -59,8 +59,6 @@ module Ameba::AST
end end
when Crystal::BinaryOp when Crystal::BinaryOp
unreachable_nodes << current_node.right if flow_expression?(current_node.left, in_loop?) unreachable_nodes << current_node.right if flow_expression?(current_node.left, in_loop?)
else
# nop
end end
unreachable_nodes unreachable_nodes

View file

@ -117,8 +117,8 @@ module Ameba::AST
# Returns true instance variable assinged in this scope. # Returns true instance variable assinged in this scope.
def assigns_ivar?(name) def assigns_ivar?(name)
arguments.find { |arg| arg.name == name } && arguments.find(&.name.== name) &&
ivariables.find { |var| var.name == "@#{name}" } ivariables.find(&.name.== "@#{name}")
end end
# Returns true if and only if current scope represents some # Returns true if and only if current scope represents some
@ -137,13 +137,13 @@ module Ameba::AST
def references?(variable : Variable) def references?(variable : Variable)
variable.references.any? do |reference| variable.references.any? do |reference|
reference.scope == self || reference.scope == self ||
inner_scopes.any?(&.references? variable) inner_scopes.any?(&.references?(variable))
end || variable.used_in_macro? end || variable.used_in_macro?
end end
# Returns true if current scope is a def, false if not. # Returns true if current scope is a def, false if not.
def def? def def?
node.is_a? Crystal::Def node.is_a?(Crystal::Def)
end end
# Returns true if this scope is a top level scope, false if not. # Returns true if this scope is a top level scope, false if not.
@ -173,7 +173,7 @@ module Ameba::AST
# the same Crystal node as `@node`. # the same Crystal node as `@node`.
def eql?(node) def eql?(node)
node == @node && node == @node &&
!node.location.nil? && node.location &&
node.location == @node.location node.location == @node.location
end end
end end

View file

@ -72,12 +72,12 @@ module Ameba::Cli
parser.on("--only RULE1,RULE2,...", parser.on("--only RULE1,RULE2,...",
"Run only given rules (or groups)") do |rules| "Run only given rules (or groups)") do |rules|
opts.only = rules.split "," opts.only = rules.split(',')
end end
parser.on("--except RULE1,RULE2,...", parser.on("--except RULE1,RULE2,...",
"Disable the given rules (or groups)") do |rules| "Disable the given rules (or groups)") do |rules|
opts.except = rules.split "," opts.except = rules.split(',')
end end
parser.on("--all", "Enables all available rules") do parser.on("--all", "Enables all available rules") do
@ -90,7 +90,8 @@ module Ameba::Cli
opts.config = "" opts.config = ""
end end
parser.on("--fail-level SEVERITY", "Change the level of failure to exit. Defaults to Convention") do |level| parser.on("--fail-level SEVERITY",
"Change the level of failure to exit. Defaults to Convention") do |level|
opts.fail_level = Severity.parse(level) opts.fail_level = Severity.parse(level)
end end
@ -113,13 +114,13 @@ module Ameba::Cli
end end
private def configure_rules(config, opts) private def configure_rules(config, opts)
if only = opts.only case
config.rules.map! { |r| r.enabled = false; r } when only = opts.only
config.rules.map! &.tap(&.enabled = false)
config.update_rules(only, enabled: true) config.update_rules(only, enabled: true)
elsif opts.all? when opts.all?
config.rules.map! { |r| r.enabled = true; r } config.rules.map! &.tap(&.enabled = true)
end end
config.update_rules(opts.except, enabled: false) config.update_rules(opts.except, enabled: false)
end end
@ -127,7 +128,8 @@ module Ameba::Cli
if name = opts.formatter if name = opts.formatter
config.formatter = name config.formatter = name
end end
config.formatter.config[:without_affected_code] = opts.without_affected_code? config.formatter.config[:without_affected_code] =
opts.without_affected_code?
end end
private def configure_explain_opts(loc, opts) private def configure_explain_opts(loc, opts)
@ -138,10 +140,15 @@ module Ameba::Cli
end end
private def parse_explain_location(arg) private def parse_explain_location(arg)
location = arg.split(":", remove_empty: true).map &.strip location = arg.split(':', remove_empty: true).map! &.strip
raise ArgumentError.new unless location.size === 3 raise ArgumentError.new unless location.size === 3
file, line, column = location file, line, column = location
{file: file, line: line.to_i, column: column.to_i} {
file: file,
line: line.to_i,
column: column.to_i,
}
rescue rescue
raise "location should have PATH:line:column format" raise "location should have PATH:line:column format"
end end

View file

@ -31,7 +31,6 @@ class Ameba::Config
!lib !lib
) )
setter formatter : Formatter::BaseFormatter?
getter rules : Array(Rule::Base) getter rules : Array(Rule::Base)
property severity = Severity::Convention property severity = Severity::Convention
@ -66,7 +65,9 @@ class Ameba::Config
@excluded = load_array_section(config, "Excluded") @excluded = load_array_section(config, "Excluded")
@globs = load_array_section(config, "Globs", DEFAULT_GLOBS) @globs = load_array_section(config, "Globs", DEFAULT_GLOBS)
self.formatter = load_formatter_name(config) if formatter_name = load_formatter_name(config)
self.formatter = formatter_name
end
end end
# Loads YAML configuration file by `path`. # Loads YAML configuration file by `path`.
@ -84,7 +85,7 @@ class Ameba::Config
end end
def self.formatter_names def self.formatter_names
AVAILABLE_FORMATTERS.keys.join("|") AVAILABLE_FORMATTERS.keys.join('|')
end end
# Returns a list of sources matching globs and excluded sections. # Returns a list of sources matching globs and excluded sections.
@ -111,8 +112,8 @@ class Ameba::Config
# config.formatter # config.formatter
# ``` # ```
# #
def formatter property formatter : Formatter::BaseFormatter do
@formatter ||= Formatter::DotFormatter.new Formatter::DotFormatter.new
end end
# Sets formatter by name. # Sets formatter by name.
@ -138,7 +139,7 @@ class Ameba::Config
# ``` # ```
# #
def update_rule(name, enabled = true, excluded = nil) def update_rule(name, enabled = true, excluded = nil)
index = @rules.index { |r| r.name == name } index = @rules.index { |rule| rule.name == name }
raise ArgumentError.new("Rule `#{name}` does not exist") unless index raise ArgumentError.new("Rule `#{name}` does not exist") unless index
rule = @rules[index] rule = @rules[index]
@ -172,7 +173,7 @@ class Ameba::Config
private def load_formatter_name(config) private def load_formatter_name(config)
name = config["Formatter"]?.try &.["Name"]? name = config["Formatter"]?.try &.["Name"]?
name ? name.to_s : nil name.try(&.to_s)
end end
private def load_array_section(config, section_name, default = [] of String) private def load_array_section(config, section_name, default = [] of String)

View file

@ -35,14 +35,17 @@ module Ameba::Formatter
next if issue.disabled? next if issue.disabled?
next if (location = issue.location).nil? next if (location = issue.location).nil?
output << "#{location}\n".colorize(:cyan) output.puts location.colorize(:cyan)
output << "[#{issue.rule.severity.symbol}] #{issue.rule.name}: #{issue.message}\n".colorize(:red) output.puts \
"[#{issue.rule.severity.symbol}] " \
"#{issue.rule.name}: " \
"#{issue.message}".colorize(:red)
if show_affected_code && (code = affected_code(source, location)) if show_affected_code && (code = affected_code(source, location))
output << code.colorize(:default) output << code.colorize(:default)
end end
output << "\n" output << '\n'
end end
end end

View file

@ -4,9 +4,8 @@ module Ameba::Formatter
# A formatter that shows the detailed explanation of the issue at # A formatter that shows the detailed explanation of the issue at
# a specific location. # a specific location.
class ExplainFormatter class ExplainFormatter
LINE_BREAK = "\n" HEADING = "## "
HEADING = "## " PREFIX = " "
PREFIX = " "
include Util include Util
@ -65,19 +64,19 @@ module Ameba::Formatter
end end
private def output_title(title) private def output_title(title)
output << HEADING.colorize(:yellow) << title.colorize(:yellow) << LINE_BREAK output << HEADING.colorize(:yellow) << title.colorize(:yellow) << '\n'
output << LINE_BREAK output << '\n'
end end
private def output_paragraph(paragraph : String) private def output_paragraph(paragraph : String)
output_paragraph(paragraph.split(LINE_BREAK)) output_paragraph(paragraph.split('\n'))
end end
private def output_paragraph(paragraph : Array(String)) private def output_paragraph(paragraph : Array(String))
paragraph.each do |line| paragraph.each do |line|
output << PREFIX << line << LINE_BREAK output << PREFIX << line << '\n'
end end
output << LINE_BREAK output << '\n'
end end
end end
end end

View file

@ -9,7 +9,7 @@ module Ameba::Formatter
@mutex.synchronize do @mutex.synchronize do
output.printf "%s:%d:%d: %s: [%s] %s\n", output.printf "%s:%d:%d: %s: [%s] %s\n",
source.path, loc.line_number, loc.column_number, e.rule.severity.symbol, source.path, loc.line_number, loc.column_number, e.rule.severity.symbol,
e.rule.name, e.message.gsub("\n", " ") e.rule.name, e.message.gsub('\n', " ")
end end
end end
end end

View file

@ -8,19 +8,20 @@ module Ameba::Formatter
def finished(sources) def finished(sources)
super super
issues = sources.map(&.issues).flatten
issues = sources.flat_map(&.issues)
unless issues.any? { |issue| !issue.disabled? } unless issues.any? { |issue| !issue.disabled? }
@output << "No issues found. File is not generated.\n" @output.puts "No issues found. File is not generated."
return return
end end
if issues.any? { |issue| issue.syntax? } if issues.any?(&.syntax?)
@output << "Unable to generate TODO file. Please fix syntax issues.\n" @output.puts "Unable to generate TODO file. Please fix syntax issues."
return return
end end
file = generate_todo_config issues file = generate_todo_config issues
@output << "Created #{file.path}\n" @output.puts "Created #{file.path}"
file file
end end
@ -57,10 +58,9 @@ module Ameba::Formatter
end end
private def rule_todo(rule, issues) private def rule_todo(rule, issues)
rule.excluded = rule.excluded = issues
issues.map(&.location.try &.filename.try &.to_s) .compact_map(&.location.try &.filename.try &.to_s)
.compact .uniq!
.uniq!
{rule.name => rule}.to_yaml {rule.name => rule}.to_yaml
end end

View file

@ -2,9 +2,9 @@ module Ameba::Formatter
module Util module Util
def affected_code(source, location, max_length = 100, placeholder = " ...", prompt = "> ") def affected_code(source, location, max_length = 100, placeholder = " ...", prompt = "> ")
line, column = location.line_number, location.column_number line, column = location.line_number, location.column_number
affected_line = source.lines[line - 1]? affected_line = source.lines[line - 1]?.presence
return if affected_line.nil? || affected_line.strip.empty? return unless affected_line
if affected_line.size > max_length && column < max_length if affected_line.size > max_length && column < max_length
affected_line = affected_line[0, max_length - placeholder.size - 1] + placeholder affected_line = affected_line[0, max_length - placeholder.size - 1] + placeholder
@ -14,7 +14,7 @@ module Ameba::Formatter
position = column - (affected_line.size - stripped.size) + prompt.size position = column - (affected_line.size - stripped.size) + prompt.size
String.build do |str| String.build do |str|
str << prompt << stripped << "\n" str << prompt << stripped << '\n'
str << " " * (position - 1) str << " " * (position - 1)
str << "^".colorize(:yellow) str << "^".colorize(:yellow)
end end

View file

@ -22,10 +22,10 @@ module Ameba
# ``` # ```
# #
def expand(globs) def expand(globs)
globs.map do |glob| globs.flat_map do |glob|
glob += "/**/*.cr" if File.directory?(glob) glob += "/**/*.cr" if File.directory?(glob)
Dir[glob] Dir[glob]
end.flatten.uniq! end.uniq!
end end
private def rejected_globs(globs) private def rejected_globs(globs)

View file

@ -89,8 +89,10 @@ module Ameba
private def line_disabled?(line, rule) private def line_disabled?(line, rule)
return false unless directive = parse_inline_directive(line) return false unless directive = parse_inline_directive(line)
Action.parse?(directive[:action]).try(&.disable?) && return false unless Action.parse?(directive[:action]).try(&.disable?)
(directive[:rules].includes?(rule.name) || directive[:rules].includes?(rule.group))
directive[:rules].includes?(rule.name) ||
directive[:rules].includes?(rule.group)
end end
private def commented_out?(line) private def commented_out?(line)

View file

@ -95,7 +95,7 @@ module Ameba::Rule
def excluded?(source) def excluded?(source)
excluded.try &.any? do |path| excluded.try &.any? do |path|
source.matches_path?(path) || source.matches_path?(path) ||
Dir.glob(path).any? { |glob| source.matches_path? glob } Dir.glob(path).any? { |glob| source.matches_path?(glob) }
end end
end end
@ -123,11 +123,11 @@ module Ameba::Rule
end end
protected def self.rule_name protected def self.rule_name
name.gsub("Ameba::Rule::", "").gsub("::", "/") name.gsub("Ameba::Rule::", "").gsub("::", '/')
end end
protected def self.group_name protected def self.group_name
rule_name.split("/")[0...-1].join("/") rule_name.split('/')[0...-1].join('/')
end end
protected def self.subclasses protected def self.subclasses
@ -157,7 +157,7 @@ module Ameba::Rule
def self.parsed_doc def self.parsed_doc
source = File.read(path_to_source_file) source = File.read(path_to_source_file)
nodes = Crystal::Parser.new(source).tap(&.wants_doc = true).parse nodes = Crystal::Parser.new(source).tap(&.wants_doc = true).parse
type_name = rule_name.split("/").last? type_name = rule_name.split('/').last?
DocFinder.new(nodes, type_name).doc DocFinder.new(nodes, type_name).doc
end end

View file

@ -49,8 +49,6 @@ module Ameba::Rule::Lint
issue_for start_token.not_nil!, issue.not_nil! issue_for start_token.not_nil!, issue.not_nil!
end end
issue = start_token = nil issue = start_token = nil
else
# nop
end end
end end
end end
@ -61,8 +59,6 @@ module Ameba::Rule::Lint
check_array_entry entry, string_array_unwanted_symbols, "%w" check_array_entry entry, string_array_unwanted_symbols, "%w"
when .starts_with? "%i" when .starts_with? "%i"
check_array_entry entry, symbol_array_unwanted_symbols, "%i" check_array_entry entry, symbol_array_unwanted_symbols, "%i"
else
# nop
end end
end end

View file

@ -42,8 +42,6 @@ module Ameba::Rule::Lint
report source, node, "Remove redundant with_index" report source, node, "Remove redundant with_index"
when "each_with_index" when "each_with_index"
report source, node, "Use each instead of each_with_index" report source, node, "Use each instead of each_with_index"
else
# nop
end end
end end

View file

@ -76,8 +76,6 @@ module Ameba::Rule::Style
redundant_begin_in_handler?(source, body, node) redundant_begin_in_handler?(source, body, node)
when Crystal::Expressions when Crystal::Expressions
redundant_begin_in_expressions?(body) redundant_begin_in_expressions?(body)
else
# nop
end end
end end
@ -88,7 +86,7 @@ module Ameba::Rule::Style
private def redundant_begin_in_handler?(source, handler, node) private def redundant_begin_in_handler?(source, handler, node)
return false if begin_exprs_in_handler?(handler) || inner_handler?(handler) return false if begin_exprs_in_handler?(handler) || inner_handler?(handler)
code = node_source(node, source.lines).try &.join("\n") code = node_source(node, source.lines).try &.join('\n')
def_redundant_begin? code if code def_redundant_begin? code if code
rescue rescue
false false

View file

@ -50,7 +50,6 @@ module Ameba
.find &.name.==(Rule::Lint::UnneededDisableDirective.rule_name) .find &.name.==(Rule::Lint::UnneededDisableDirective.rule_name)
end end
# :nodoc:
protected def initialize(@rules, @sources, @formatter, @severity) protected def initialize(@rules, @sources, @formatter, @severity)
end end
@ -80,9 +79,8 @@ module Ameba
end end
end end
channels.each do |c| channels.each do |chan|
e = c.receive chan.receive.try { |e| raise e }
raise e unless e.nil?
end end
self self

View file

@ -9,8 +9,12 @@ module Ameba
# ``` # ```
# Severity::Warning.symbol # => 'W' # Severity::Warning.symbol # => 'W'
# ``` # ```
def symbol def symbol : Char
to_s[0] case self
in Error then 'E'
in Warning then 'W'
in Convention then 'C'
end
end end
# Creates Severity by the name. # Creates Severity by the name.

View file

@ -11,10 +11,6 @@ module Ameba
# Crystal code (content of a source file). # Crystal code (content of a source file).
getter code : String getter code : String
@lines : Array(String)?
@ast : Crystal::ASTNode?
@fullpath : String?
# Creates a new source by `code` and `path`. # Creates a new source by `code` and `path`.
# #
# For example: # For example:
@ -24,7 +20,7 @@ module Ameba
# Ameba::Source.new File.read(path), path # Ameba::Source.new File.read(path), path
# ``` # ```
# #
def initialize(@code : String, @path = "") def initialize(@code, @path = "")
end end
# Returns lines of code splitted by new line character. # Returns lines of code splitted by new line character.
@ -38,9 +34,7 @@ module Ameba
# source.lines # => ["a = 1", "b = 2"] # source.lines # => ["a = 1", "b = 2"]
# ``` # ```
# #
def lines getter lines : Array(String) { code.split('\n') }
@lines ||= @code.split("\n")
end
# Returns AST nodes constructed by `Crystal::Parser`. # Returns AST nodes constructed by `Crystal::Parser`.
# #
@ -49,16 +43,15 @@ module Ameba
# source.ast # source.ast
# ``` # ```
# #
def ast getter ast : Crystal::ASTNode do
@ast ||= Crystal::Parser.new(code)
Crystal::Parser.new(code) .tap(&.wants_doc = true)
.tap { |parser| parser.wants_doc = true } .tap(&.filename = path)
.tap { |parser| parser.filename = @path } .parse
.parse
end end
def fullpath getter fullpath : String do
@fullpath ||= File.expand_path @path File.expand_path(path)
end end
# Returns true if *filepath* matches the source's path, false if it does not. # Returns true if *filepath* matches the source's path, false if it does not.

View file

@ -53,8 +53,7 @@ module Ameba
false false
end end
private def run_normal_state(lexer, break_on_rcurly = false, private def run_normal_state(lexer, break_on_rcurly = false, &block : Crystal::Token -> _)
&block : Crystal::Token -> _)
loop do loop do
token = @lexer.next_token token = @lexer.next_token
block.call token block.call token
@ -68,8 +67,6 @@ module Ameba
break break
when :"}" when :"}"
break if break_on_rcurly break if break_on_rcurly
else
# go on
end end
end end
end end
@ -86,8 +83,6 @@ module Ameba
run_normal_state lexer, break_on_rcurly: true, &block run_normal_state lexer, break_on_rcurly: true, &block
when :EOF when :EOF
break break
else
# go on
end end
end end
end end
@ -102,8 +97,6 @@ module Ameba
break break
when :EOF when :EOF
break break
else
# go on
end end
end end
end end