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
|
||||
it "returns nil if there is no such a line number" do
|
||||
source = Source.new %(
|
||||
code = <<-EOF
|
||||
a = 1
|
||||
)
|
||||
EOF
|
||||
location = Crystal::Location.new("filename", 2, 1)
|
||||
subject.affected_code(source, location).should be_nil
|
||||
subject.affected_code(code, location).should be_nil
|
||||
end
|
||||
|
||||
it "returns correct line if it is found" do
|
||||
source = Source.new %(
|
||||
code = <<-EOF
|
||||
a = 1
|
||||
)
|
||||
EOF
|
||||
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"
|
||||
end
|
||||
|
||||
it "returns correct line if it is found" do
|
||||
source = Source.new <<-EOF
|
||||
code = <<-EOF
|
||||
# pre:1
|
||||
# pre:2
|
||||
# pre:3
|
||||
|
@ -101,7 +101,7 @@ module Ameba::Formatter
|
|||
EOF
|
||||
|
||||
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
|
||||
> # pre:3
|
||||
> # pre:4
|
||||
|
|
|
@ -3,7 +3,8 @@ require "../spec_helper"
|
|||
module Ameba
|
||||
describe Issue do
|
||||
it "accepts rule and message" do
|
||||
issue = Issue.new rule: DummyRule.new,
|
||||
issue = Issue.new code: "",
|
||||
rule: DummyRule.new,
|
||||
location: nil,
|
||||
end_location: nil,
|
||||
message: "Blah",
|
||||
|
@ -15,7 +16,8 @@ module Ameba
|
|||
|
||||
it "accepts location" do
|
||||
location = Crystal::Location.new("path", 3, 2)
|
||||
issue = Issue.new rule: DummyRule.new,
|
||||
issue = Issue.new code: "",
|
||||
rule: DummyRule.new,
|
||||
location: location,
|
||||
end_location: nil,
|
||||
message: "Blah",
|
||||
|
@ -27,7 +29,8 @@ module Ameba
|
|||
|
||||
it "accepts end_location" do
|
||||
location = Crystal::Location.new("path", 3, 2)
|
||||
issue = Issue.new rule: DummyRule.new,
|
||||
issue = Issue.new code: "",
|
||||
rule: DummyRule.new,
|
||||
location: nil,
|
||||
end_location: location,
|
||||
message: "Blah",
|
||||
|
@ -38,7 +41,8 @@ module Ameba
|
|||
end
|
||||
|
||||
it "accepts status" do
|
||||
issue = Issue.new rule: DummyRule.new,
|
||||
issue = Issue.new code: "",
|
||||
rule: DummyRule.new,
|
||||
location: nil,
|
||||
end_location: nil,
|
||||
message: "",
|
||||
|
@ -50,7 +54,8 @@ module Ameba
|
|||
end
|
||||
|
||||
it "sets status to :enabled by default" do
|
||||
issue = Issue.new rule: DummyRule.new,
|
||||
issue = Issue.new code: "",
|
||||
rule: DummyRule.new,
|
||||
location: nil,
|
||||
end_location: nil,
|
||||
message: ""
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
private def dummy_issue(message,
|
||||
private def dummy_issue(code,
|
||||
message,
|
||||
position : {Int32, Int32}?,
|
||||
end_position : {Int32, Int32}?,
|
||||
path = "")
|
||||
|
@ -9,6 +10,7 @@ private def dummy_issue(message,
|
|||
end_location = Crystal::Location.new(path, *end_position) if end_position
|
||||
|
||||
Ameba::Issue.new(
|
||||
code: code,
|
||||
rule: Ameba::DummyRule.new,
|
||||
location: location,
|
||||
end_location: end_location,
|
||||
|
@ -25,7 +27,7 @@ private def expect_invalid_location(code,
|
|||
expect_raises Exception, exception_message, file, line do
|
||||
Ameba::Spec::AnnotatedSource.new(
|
||||
lines: code.lines,
|
||||
issues: [dummy_issue("Message", position, end_position, "path")]
|
||||
issues: [dummy_issue(code, "Message", position, end_position, "path")]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,7 +50,7 @@ module Ameba::Formatter
|
|||
"#{issue.rule.name}: " \
|
||||
"#{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)
|
||||
end
|
||||
|
||||
|
|
|
@ -38,10 +38,7 @@ module Ameba::Formatter
|
|||
private def explain(source, issue)
|
||||
rule = issue.rule
|
||||
|
||||
location, end_location =
|
||||
issue.location, issue.end_location
|
||||
|
||||
return unless location
|
||||
return unless (location = issue.location)
|
||||
|
||||
output_title "ISSUE INFO"
|
||||
output_paragraph [
|
||||
|
@ -49,7 +46,7 @@ module Ameba::Formatter
|
|||
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_paragraph affected_code
|
||||
end
|
||||
|
|
|
@ -40,8 +40,14 @@ module Ameba::Formatter
|
|||
{pre_context, post_context}
|
||||
end
|
||||
|
||||
def affected_code(source, location, end_location = nil, context_lines = 0, max_length = 120, ellipsis = " ...", prompt = "> ")
|
||||
lines = source.lines
|
||||
def affected_code(issue : Issue, context_lines = 0, max_length = 120, ellipsis = " ...", prompt = "> ")
|
||||
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 =
|
||||
location.line_number, location.column_number
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ module Ameba
|
|||
Disabled
|
||||
end
|
||||
|
||||
# The source code that triggered this issue.
|
||||
getter code : String
|
||||
|
||||
# A rule that triggers this issue.
|
||||
getter rule : Rule::Base
|
||||
|
||||
|
@ -24,7 +27,7 @@ module Ameba
|
|||
delegate :enabled?, :disabled?,
|
||||
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
|
||||
end
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ module Ameba
|
|||
status ||=
|
||||
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
|
||||
end
|
||||
end
|
||||
|
|
|
@ -93,17 +93,23 @@ module Ameba
|
|||
private def run_source(source)
|
||||
@formatter.source_started source
|
||||
|
||||
# TODO: run autocorrection recursively. A new `Issue#source` property must
|
||||
# be added so that `affected_code` will return the code from the old
|
||||
# source instead of the autocorrected one.
|
||||
if @syntax_rule.catch(source).valid?
|
||||
corrected_issues = [] of Issue
|
||||
loop do
|
||||
@syntax_rule.test(source)
|
||||
break unless source.valid?
|
||||
|
||||
@rules.each do |rule|
|
||||
next if rule.excluded?(source)
|
||||
rule.test(source)
|
||||
end
|
||||
check_unneeded_directives(source)
|
||||
autocorrect(source) if autocorrect?
|
||||
break unless autocorrect? && source.correct
|
||||
|
||||
corrected_issues += source.issues.select(&.correctable?)
|
||||
source.issues.clear
|
||||
end
|
||||
corrected_issues.reverse_each { |issue| source.issues.unshift(issue) }
|
||||
File.write(source.path, source.code) if corrected_issues.any?
|
||||
|
||||
@formatter.source_finished source
|
||||
end
|
||||
|
@ -143,12 +149,5 @@ module Ameba
|
|||
rule.test(source)
|
||||
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
|
||||
|
|
|
@ -11,6 +11,9 @@ module Ameba
|
|||
# Crystal code (content of a source file).
|
||||
getter code : String
|
||||
|
||||
@lines : Array(String)?
|
||||
@ast : Crystal::ASTNode?
|
||||
|
||||
# Creates a new source by `code` and `path`.
|
||||
#
|
||||
# For example:
|
||||
|
@ -22,6 +25,18 @@ module Ameba
|
|||
def initialize(@code, @path = "")
|
||||
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.
|
||||
# Since `code` is immutable and can't be changed, this
|
||||
# method caches lines in an instance variable, so calling
|
||||
|
@ -33,7 +48,9 @@ module Ameba
|
|||
# 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`.
|
||||
#
|
||||
|
@ -42,11 +59,12 @@ module Ameba
|
|||
# source.ast
|
||||
# ```
|
||||
#
|
||||
getter ast : Crystal::ASTNode do
|
||||
Crystal::Parser.new(code)
|
||||
.tap(&.wants_doc = true)
|
||||
.tap(&.filename = path)
|
||||
.parse
|
||||
def ast : Crystal::ASTNode
|
||||
@ast ||=
|
||||
Crystal::Parser.new(code)
|
||||
.tap(&.wants_doc = true)
|
||||
.tap(&.filename = path)
|
||||
.parse
|
||||
end
|
||||
|
||||
getter fullpath : String do
|
||||
|
|
|
@ -2,16 +2,6 @@ require "./rewriter"
|
|||
|
||||
class Ameba::Source
|
||||
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)
|
||||
|
||||
def initialize(code : String)
|
||||
|
|
|
@ -85,9 +85,8 @@ module Ameba::Spec::ExpectIssue
|
|||
source = ExpectIssue.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 corrected_code
|
||||
return if correction == corrected_code
|
||||
raise "Use `expect_no_corrections` if the code will not change" unless source.correct
|
||||
return if correction == source.code
|
||||
|
||||
fail <<-MSG, file, line
|
||||
Expected correction:
|
||||
|
@ -96,7 +95,7 @@ module Ameba::Spec::ExpectIssue
|
|||
|
||||
Got:
|
||||
|
||||
#{corrected_code}
|
||||
#{source.code}
|
||||
MSG
|
||||
end
|
||||
|
||||
|
@ -104,13 +103,12 @@ module Ameba::Spec::ExpectIssue
|
|||
source = ExpectIssue.source
|
||||
raise "`expect_no_corrections` must follow `expect_offense`" unless source
|
||||
|
||||
corrected_code = Source::Corrector.correct(source)
|
||||
return unless corrected_code
|
||||
return unless source.correct
|
||||
|
||||
fail <<-MSG, file, line
|
||||
Expected no corrections, but got:
|
||||
|
||||
#{corrected_code}
|
||||
#{source.code}
|
||||
MSG
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue