mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Re-run autocorrect until all correctable issues have been corrected
This commit is contained in:
parent
573881cb8a
commit
f39a7a4cd4
12 changed files with 78 additions and 60 deletions
|
@ -69,24 +69,24 @@ module Ameba::Formatter
|
||||||
|
|
||||||
describe "#affected_code" do
|
describe "#affected_code" do
|
||||||
it "returns nil if there is no such a line number" do
|
it "returns nil if there is no such a line number" do
|
||||||
source = Source.new %(
|
code = <<-EOF
|
||||||
a = 1
|
a = 1
|
||||||
)
|
EOF
|
||||||
location = Crystal::Location.new("filename", 2, 1)
|
location = Crystal::Location.new("filename", 2, 1)
|
||||||
subject.affected_code(source, location).should be_nil
|
subject.affected_code(code, location).should be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns correct line if it is found" do
|
it "returns correct line if it is found" do
|
||||||
source = Source.new %(
|
code = <<-EOF
|
||||||
a = 1
|
a = 1
|
||||||
)
|
EOF
|
||||||
location = Crystal::Location.new("filename", 1, 1)
|
location = Crystal::Location.new("filename", 1, 1)
|
||||||
subject.deansify(subject.affected_code(source, location))
|
subject.deansify(subject.affected_code(code, location))
|
||||||
.should eq "> a = 1\n ^\n"
|
.should eq "> a = 1\n ^\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns correct line if it is found" do
|
it "returns correct line if it is found" do
|
||||||
source = Source.new <<-EOF
|
code = <<-EOF
|
||||||
# pre:1
|
# pre:1
|
||||||
# pre:2
|
# pre:2
|
||||||
# pre:3
|
# pre:3
|
||||||
|
@ -101,7 +101,7 @@ module Ameba::Formatter
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
location = Crystal::Location.new("filename", 6, 1)
|
location = Crystal::Location.new("filename", 6, 1)
|
||||||
subject.deansify(subject.affected_code(source, location, context_lines: 3))
|
subject.deansify(subject.affected_code(code, location, context_lines: 3))
|
||||||
.should eq <<-STR
|
.should eq <<-STR
|
||||||
> # pre:3
|
> # pre:3
|
||||||
> # pre:4
|
> # pre:4
|
||||||
|
|
|
@ -3,7 +3,8 @@ require "../spec_helper"
|
||||||
module Ameba
|
module Ameba
|
||||||
describe Issue do
|
describe Issue do
|
||||||
it "accepts rule and message" do
|
it "accepts rule and message" do
|
||||||
issue = Issue.new rule: DummyRule.new,
|
issue = Issue.new code: "",
|
||||||
|
rule: DummyRule.new,
|
||||||
location: nil,
|
location: nil,
|
||||||
end_location: nil,
|
end_location: nil,
|
||||||
message: "Blah",
|
message: "Blah",
|
||||||
|
@ -15,7 +16,8 @@ module Ameba
|
||||||
|
|
||||||
it "accepts location" do
|
it "accepts location" do
|
||||||
location = Crystal::Location.new("path", 3, 2)
|
location = Crystal::Location.new("path", 3, 2)
|
||||||
issue = Issue.new rule: DummyRule.new,
|
issue = Issue.new code: "",
|
||||||
|
rule: DummyRule.new,
|
||||||
location: location,
|
location: location,
|
||||||
end_location: nil,
|
end_location: nil,
|
||||||
message: "Blah",
|
message: "Blah",
|
||||||
|
@ -27,7 +29,8 @@ module Ameba
|
||||||
|
|
||||||
it "accepts end_location" do
|
it "accepts end_location" do
|
||||||
location = Crystal::Location.new("path", 3, 2)
|
location = Crystal::Location.new("path", 3, 2)
|
||||||
issue = Issue.new rule: DummyRule.new,
|
issue = Issue.new code: "",
|
||||||
|
rule: DummyRule.new,
|
||||||
location: nil,
|
location: nil,
|
||||||
end_location: location,
|
end_location: location,
|
||||||
message: "Blah",
|
message: "Blah",
|
||||||
|
@ -38,7 +41,8 @@ module Ameba
|
||||||
end
|
end
|
||||||
|
|
||||||
it "accepts status" do
|
it "accepts status" do
|
||||||
issue = Issue.new rule: DummyRule.new,
|
issue = Issue.new code: "",
|
||||||
|
rule: DummyRule.new,
|
||||||
location: nil,
|
location: nil,
|
||||||
end_location: nil,
|
end_location: nil,
|
||||||
message: "",
|
message: "",
|
||||||
|
@ -50,7 +54,8 @@ module Ameba
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sets status to :enabled by default" do
|
it "sets status to :enabled by default" do
|
||||||
issue = Issue.new rule: DummyRule.new,
|
issue = Issue.new code: "",
|
||||||
|
rule: DummyRule.new,
|
||||||
location: nil,
|
location: nil,
|
||||||
end_location: nil,
|
end_location: nil,
|
||||||
message: ""
|
message: ""
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
require "../../spec_helper"
|
require "../../spec_helper"
|
||||||
|
|
||||||
private def dummy_issue(message,
|
private def dummy_issue(code,
|
||||||
|
message,
|
||||||
position : {Int32, Int32}?,
|
position : {Int32, Int32}?,
|
||||||
end_position : {Int32, Int32}?,
|
end_position : {Int32, Int32}?,
|
||||||
path = "")
|
path = "")
|
||||||
|
@ -9,6 +10,7 @@ private def dummy_issue(message,
|
||||||
end_location = Crystal::Location.new(path, *end_position) if end_position
|
end_location = Crystal::Location.new(path, *end_position) if end_position
|
||||||
|
|
||||||
Ameba::Issue.new(
|
Ameba::Issue.new(
|
||||||
|
code: code,
|
||||||
rule: Ameba::DummyRule.new,
|
rule: Ameba::DummyRule.new,
|
||||||
location: location,
|
location: location,
|
||||||
end_location: end_location,
|
end_location: end_location,
|
||||||
|
@ -25,7 +27,7 @@ private def expect_invalid_location(code,
|
||||||
expect_raises Exception, exception_message, file, line do
|
expect_raises Exception, exception_message, file, line do
|
||||||
Ameba::Spec::AnnotatedSource.new(
|
Ameba::Spec::AnnotatedSource.new(
|
||||||
lines: code.lines,
|
lines: code.lines,
|
||||||
issues: [dummy_issue("Message", position, end_position, "path")]
|
issues: [dummy_issue(code, "Message", position, end_position, "path")]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,7 +50,7 @@ module Ameba::Formatter
|
||||||
"#{issue.rule.name}: " \
|
"#{issue.rule.name}: " \
|
||||||
"#{issue.message}".colorize(:red)
|
"#{issue.message}".colorize(:red)
|
||||||
|
|
||||||
if show_affected_code && (code = affected_code(source, location, issue.end_location))
|
if show_affected_code && (code = affected_code(issue))
|
||||||
output << code.colorize(:default)
|
output << code.colorize(:default)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -38,10 +38,7 @@ module Ameba::Formatter
|
||||||
private def explain(source, issue)
|
private def explain(source, issue)
|
||||||
rule = issue.rule
|
rule = issue.rule
|
||||||
|
|
||||||
location, end_location =
|
return unless (location = issue.location)
|
||||||
issue.location, issue.end_location
|
|
||||||
|
|
||||||
return unless location
|
|
||||||
|
|
||||||
output_title "ISSUE INFO"
|
output_title "ISSUE INFO"
|
||||||
output_paragraph [
|
output_paragraph [
|
||||||
|
@ -49,7 +46,7 @@ module Ameba::Formatter
|
||||||
location.to_s.colorize(:cyan).to_s,
|
location.to_s.colorize(:cyan).to_s,
|
||||||
]
|
]
|
||||||
|
|
||||||
if affected_code = affected_code(source, location, end_location, context_lines: 3)
|
if affected_code = affected_code(issue, context_lines: 3)
|
||||||
output_title "AFFECTED CODE"
|
output_title "AFFECTED CODE"
|
||||||
output_paragraph affected_code
|
output_paragraph affected_code
|
||||||
end
|
end
|
||||||
|
|
|
@ -40,8 +40,14 @@ module Ameba::Formatter
|
||||||
{pre_context, post_context}
|
{pre_context, post_context}
|
||||||
end
|
end
|
||||||
|
|
||||||
def affected_code(source, location, end_location = nil, context_lines = 0, max_length = 120, ellipsis = " ...", prompt = "> ")
|
def affected_code(issue : Issue, context_lines = 0, max_length = 120, ellipsis = " ...", prompt = "> ")
|
||||||
lines = source.lines
|
return unless (location = issue.location)
|
||||||
|
|
||||||
|
affected_code(issue.code, location, issue.end_location, context_lines, max_length, ellipsis, prompt)
|
||||||
|
end
|
||||||
|
|
||||||
|
def affected_code(code, location, end_location = nil, context_lines = 0, max_length = 120, ellipsis = " ...", prompt = "> ")
|
||||||
|
lines = code.split('\n') # must preserve trailing newline
|
||||||
lineno, column =
|
lineno, column =
|
||||||
location.line_number, location.column_number
|
location.line_number, location.column_number
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@ module Ameba
|
||||||
Disabled
|
Disabled
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The source code that triggered this issue.
|
||||||
|
getter code : String
|
||||||
|
|
||||||
# A rule that triggers this issue.
|
# A rule that triggers this issue.
|
||||||
getter rule : Rule::Base
|
getter rule : Rule::Base
|
||||||
|
|
||||||
|
@ -24,7 +27,7 @@ module Ameba
|
||||||
delegate :enabled?, :disabled?,
|
delegate :enabled?, :disabled?,
|
||||||
to: status
|
to: status
|
||||||
|
|
||||||
def initialize(@rule, @location, @end_location, @message, status : Status? = nil, @block : (Source::Corrector ->)? = nil)
|
def initialize(@code, @rule, @location, @end_location, @message, status : Status? = nil, @block : (Source::Corrector ->)? = nil)
|
||||||
@status = status || Status::Enabled
|
@status = status || Status::Enabled
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Ameba
|
||||||
status ||=
|
status ||=
|
||||||
Issue::Status::Disabled if location_disabled?(location, rule)
|
Issue::Status::Disabled if location_disabled?(location, rule)
|
||||||
|
|
||||||
Issue.new(rule, location, end_location, message, status, block).tap do |issue|
|
Issue.new(code, rule, location, end_location, message, status, block).tap do |issue|
|
||||||
issues << issue
|
issues << issue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -93,17 +93,23 @@ module Ameba
|
||||||
private def run_source(source)
|
private def run_source(source)
|
||||||
@formatter.source_started source
|
@formatter.source_started source
|
||||||
|
|
||||||
# TODO: run autocorrection recursively. A new `Issue#source` property must
|
corrected_issues = [] of Issue
|
||||||
# be added so that `affected_code` will return the code from the old
|
loop do
|
||||||
# source instead of the autocorrected one.
|
@syntax_rule.test(source)
|
||||||
if @syntax_rule.catch(source).valid?
|
break unless source.valid?
|
||||||
|
|
||||||
@rules.each do |rule|
|
@rules.each do |rule|
|
||||||
next if rule.excluded?(source)
|
next if rule.excluded?(source)
|
||||||
rule.test(source)
|
rule.test(source)
|
||||||
end
|
end
|
||||||
check_unneeded_directives(source)
|
check_unneeded_directives(source)
|
||||||
autocorrect(source) if autocorrect?
|
break unless autocorrect? && source.correct
|
||||||
|
|
||||||
|
corrected_issues += source.issues.select(&.correctable?)
|
||||||
|
source.issues.clear
|
||||||
end
|
end
|
||||||
|
corrected_issues.reverse_each { |issue| source.issues.unshift(issue) }
|
||||||
|
File.write(source.path, source.code) if corrected_issues.any?
|
||||||
|
|
||||||
@formatter.source_finished source
|
@formatter.source_finished source
|
||||||
end
|
end
|
||||||
|
@ -143,12 +149,5 @@ module Ameba
|
||||||
rule.test(source)
|
rule.test(source)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def autocorrect(source)
|
|
||||||
corrected_code = Source::Corrector.correct(source)
|
|
||||||
return unless corrected_code
|
|
||||||
|
|
||||||
File.write(source.path, corrected_code)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,6 +11,9 @@ 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?
|
||||||
|
|
||||||
# Creates a new source by `code` and `path`.
|
# Creates a new source by `code` and `path`.
|
||||||
#
|
#
|
||||||
# For example:
|
# For example:
|
||||||
|
@ -22,6 +25,18 @@ module Ameba
|
||||||
def initialize(@code, @path = "")
|
def initialize(@code, @path = "")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def correct
|
||||||
|
corrector = Corrector.new(code)
|
||||||
|
issues.each(&.correct(corrector))
|
||||||
|
corrected_code = corrector.process
|
||||||
|
return false if code == corrected_code
|
||||||
|
|
||||||
|
@code = corrected_code
|
||||||
|
@lines = nil
|
||||||
|
@ast = nil
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
# Returns lines of code splitted by new line character.
|
# Returns lines of code splitted by new line character.
|
||||||
# Since `code` is immutable and can't be changed, this
|
# Since `code` is immutable and can't be changed, this
|
||||||
# method caches lines in an instance variable, so calling
|
# method caches lines in an instance variable, so calling
|
||||||
|
@ -33,7 +48,9 @@ module Ameba
|
||||||
# source.lines # => ["a = 1", "b = 2"]
|
# source.lines # => ["a = 1", "b = 2"]
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
getter lines : Array(String) { code.split('\n') }
|
def lines : Array(String)
|
||||||
|
@lines ||= code.split('\n')
|
||||||
|
end
|
||||||
|
|
||||||
# Returns AST nodes constructed by `Crystal::Parser`.
|
# Returns AST nodes constructed by `Crystal::Parser`.
|
||||||
#
|
#
|
||||||
|
@ -42,7 +59,8 @@ module Ameba
|
||||||
# source.ast
|
# source.ast
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
getter ast : Crystal::ASTNode do
|
def ast : Crystal::ASTNode
|
||||||
|
@ast ||=
|
||||||
Crystal::Parser.new(code)
|
Crystal::Parser.new(code)
|
||||||
.tap(&.wants_doc = true)
|
.tap(&.wants_doc = true)
|
||||||
.tap(&.filename = path)
|
.tap(&.filename = path)
|
||||||
|
|
|
@ -2,16 +2,6 @@ require "./rewriter"
|
||||||
|
|
||||||
class Ameba::Source
|
class Ameba::Source
|
||||||
class Corrector
|
class Corrector
|
||||||
def self.correct(source : Source)
|
|
||||||
code = source.code
|
|
||||||
corrector = new(code)
|
|
||||||
source.issues.each(&.correct(corrector))
|
|
||||||
corrected_code = corrector.process
|
|
||||||
return if code == corrected_code
|
|
||||||
|
|
||||||
corrected_code
|
|
||||||
end
|
|
||||||
|
|
||||||
@line_sizes : Array(Int32)
|
@line_sizes : Array(Int32)
|
||||||
|
|
||||||
def initialize(code : String)
|
def initialize(code : String)
|
||||||
|
|
|
@ -85,9 +85,8 @@ module Ameba::Spec::ExpectIssue
|
||||||
source = ExpectIssue.source
|
source = ExpectIssue.source
|
||||||
raise "`expect_correction` must follow `expect_issue`" unless source
|
raise "`expect_correction` must follow `expect_issue`" unless source
|
||||||
|
|
||||||
corrected_code = Source::Corrector.correct(source) # TODO: recursive
|
raise "Use `expect_no_corrections` if the code will not change" unless source.correct
|
||||||
raise "Use `expect_no_corrections` if the code will not change" unless corrected_code
|
return if correction == source.code
|
||||||
return if correction == corrected_code
|
|
||||||
|
|
||||||
fail <<-MSG, file, line
|
fail <<-MSG, file, line
|
||||||
Expected correction:
|
Expected correction:
|
||||||
|
@ -96,7 +95,7 @@ module Ameba::Spec::ExpectIssue
|
||||||
|
|
||||||
Got:
|
Got:
|
||||||
|
|
||||||
#{corrected_code}
|
#{source.code}
|
||||||
MSG
|
MSG
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -104,13 +103,12 @@ module Ameba::Spec::ExpectIssue
|
||||||
source = ExpectIssue.source
|
source = ExpectIssue.source
|
||||||
raise "`expect_no_corrections` must follow `expect_offense`" unless source
|
raise "`expect_no_corrections` must follow `expect_offense`" unless source
|
||||||
|
|
||||||
corrected_code = Source::Corrector.correct(source)
|
return unless source.correct
|
||||||
return unless corrected_code
|
|
||||||
|
|
||||||
fail <<-MSG, file, line
|
fail <<-MSG, file, line
|
||||||
Expected no corrections, but got:
|
Expected no corrections, but got:
|
||||||
|
|
||||||
#{corrected_code}
|
#{source.code}
|
||||||
MSG
|
MSG
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue