Source#content -> Source#code, Source::Error#pos -> Source::Error#location

This commit is contained in:
Vitalii Elenhaupt 2017-11-07 22:02:51 +02:00
parent 9036a7ca71
commit e718c90f16
No known key found for this signature in database
GPG key ID: 7558EF3A4056C706
39 changed files with 91 additions and 90 deletions

View file

@ -84,7 +84,7 @@ struct DebuggerStatement < Rule
# to remove a debugger statement.
return unless node.name == "debugger" && node.args.empty? && node.obj.nil?
source.error self, node.location.try &.line_number,
source.error self, node.location,
"Possible forgotten debugger statement detected"
end
end

View file

@ -20,7 +20,7 @@ module Ameba::Formatter
it "writes invalid source" do
s = Source.new ""
s.error DummyRule.new, 3, "message"
s.error DummyRule.new, nil, "message"
subject.source_finished s
output.to_s.should contain "F"
end

View file

@ -63,12 +63,12 @@ module Ameba::Rules
end
it "reports rule, pos and message" do
source = Source.new "a != true"
source = Source.new "a != true", "source.cr"
subject.catch(source)
error = source.errors.first
error.rule.should_not be_nil
error.pos.should eq 1
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Comparison to a boolean is pointless"
end
end
@ -100,12 +100,12 @@ module Ameba::Rules
end
it "reports rule, pos and message" do
source = Source.new "true != a"
source = Source.new "true != a", "source.cr"
subject.catch(source).should_not be_valid
error = source.errors.first
error.rule.should_not be_nil
error.pos.should eq 1
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Comparison to a boolean is pointless"
end
end

View file

@ -3,9 +3,9 @@ require "../../spec_helper"
module Ameba
subject = Rules::ConstantNames.new
private def it_reports_constant(content, expected)
private def it_reports_constant(code, expected)
it "reports constant name #{expected}" do
s = Source.new content
s = Source.new code
Rules::ConstantNames.new.catch(s).should_not be_valid
s.errors.first.message.should contain expected
end
@ -38,11 +38,11 @@ module Ameba
it "reports rule, pos and message" do
s = Source.new %(
Const = 1
)
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 2
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq(
"Constant name should be screaming-cased: CONST, not Const"
)

View file

@ -32,12 +32,12 @@ module Ameba::Rules
end
it "reports rule, pos and message" do
s = Source.new "debugger"
s = Source.new "debugger", "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 1
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Possible forgotten debugger statement detected"
end
end

View file

@ -70,15 +70,15 @@ module Ameba
end
)
it "reports rule, pos and message" do
it "reports rule, location and message" do
s = Source.new %(
if ()
end
)
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 2
error.location.to_s.should eq "source.cr:2:12"
error.message.should eq "Avoid empty expression '()'"
end
end

View file

@ -107,11 +107,11 @@ module Ameba
it "reports rule, pos and message" do
s = Source.new %q(
1200000
)
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 2
error.location.to_s.should eq "source.cr:2:10"
error.message.should match /1_200_000/
end
end

View file

@ -21,13 +21,13 @@ module Ameba::Rules
end
it "reports rule, pos and message" do
source = Source.new long_line
source = Source.new long_line, "source.cr"
subject.catch(source).should_not be_valid
error = source.errors.first
error.rule.should eq subject
error.pos.should eq 1
error.message.should eq "Line too long (81 symbols)"
error.location.to_s.should eq "source.cr:1:81"
error.message.should eq "Line too long"
end
end
end

View file

@ -59,13 +59,13 @@ module Ameba::Rules
it "reports rule, pos and message" do
s = Source.new %(
puts "hello" if true
)
), "source.cr"
subject.catch(s).should_not be_valid
s.errors.size.should eq 1
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 2
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq "Literal value found in conditional"
end
end

View file

@ -29,12 +29,12 @@ module Ameba::Rules
end
it "reports rule, pos and message" do
s = Source.new %q("#{4}")
s = Source.new %q("#{4}"), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 1
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Literal value found in interpolation"
end
end

View file

@ -3,9 +3,9 @@ require "../../spec_helper"
module Ameba
subject = Rules::MethodNames.new
private def it_reports_method_name(content, expected)
private def it_reports_method_name(code, expected)
it "reports method name #{expected}" do
s = Source.new content
s = Source.new code
Rules::MethodNames.new.catch(s).should_not be_valid
s.errors.first.message.should contain expected
end
@ -42,11 +42,11 @@ module Ameba
s = Source.new %(
def bad_Name
end
)
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 2
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq(
"Method name should be underscore-cased: bad_name, not bad_Name"
)

View file

@ -56,12 +56,12 @@ module Ameba::Rules
end
it "reports rule, pos and message" do
s = Source.new ":nok unless !s.empty?"
s = Source.new ":nok unless !s.empty?", "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 1
error.location.to_s.should eq "source.cr:1:1"
error.message.should eq "Avoid negated conditions in unless blocks"
end
end

View file

@ -32,12 +32,12 @@ module Ameba::Rules
true
end
end
)
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 3
error.location.to_s.should eq "source.cr:3:11"
error.message.should eq(
"Favour method name 'picture?' over 'has_picture?'")
end

View file

@ -25,12 +25,12 @@ module Ameba::Rules
end
it "reports rule, pos and message" do
source = Source.new "a = 1\n\n "
source = Source.new "a = 1\n\n ", "source.cr"
subject.catch(source).should_not be_valid
error = source.errors.first
error.rule.should_not be_nil
error.pos.should eq 3
error.location.to_s.should eq "source.cr:3:1"
error.message.should eq "Blank lines detected at the end of the file"
end
end

View file

@ -15,12 +15,12 @@ module Ameba::Rules
end
it "reports rule, pos and message" do
source = Source.new "a = 1\n b = 2 "
source = Source.new "a = 1\n b = 2 ", "source.cr"
subject.catch(source).should_not be_valid
error = source.errors.first
error.rule.should_not be_nil
error.pos.should eq 2
error.location.to_s.should eq "source.cr:2:7"
error.message.should eq "Trailing whitespace detected"
end
end

View file

@ -3,9 +3,9 @@ require "../../spec_helper"
module Ameba
subject = Rules::TypeNames.new
private def it_reports_name(content, expected)
private def it_reports_name(code, expected)
it "reports type name #{expected}" do
s = Source.new content
s = Source.new code
Rules::TypeNames.new.catch(s).should_not be_valid
s.errors.first.message.should contain expected
end
@ -47,11 +47,11 @@ module Ameba
s = Source.new %(
class My_class
end
)
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 2
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq(
"Type name should be camelcased: MyClass, but it was My_class"
)

View file

@ -31,13 +31,13 @@ module Ameba::Rules
else
:two
end
)
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.should_not be_nil
error.rule.should_not be_nil
error.pos.should eq 2
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq "Favour if over unless with else"
end
end

View file

@ -3,9 +3,9 @@ require "../../spec_helper"
module Ameba
subject = Rules::VariableNames.new
private def it_reports_var_name(content, expected)
private def it_reports_var_name(code, expected)
it "reports method name #{expected}" do
s = Source.new content
s = Source.new code
Rules::VariableNames.new.catch(s).should_not be_valid
s.errors.first.message.should contain expected
end
@ -48,11 +48,11 @@ module Ameba
it "reports rule, pos and message" do
s = Source.new %(
badName = "Yeah"
)
), "source.cr"
subject.catch(s).should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.pos.should eq 2
error.location.to_s.should eq "source.cr:2:9"
error.message.should eq(
"Var name should be underscore-cased: bad_name, not badName"
)

View file

@ -3,22 +3,24 @@ require "../spec_helper"
module Ameba
describe Source do
describe ".new" do
it "allows to create a source by content and path" do
s = Source.new("content", "path")
it "allows to create a source by code and path" do
s = Source.new("code", "path")
s.path.should eq "path"
s.content.should eq "content"
s.lines.should eq ["content"]
s.code.should eq "code"
s.lines.should eq ["code"]
end
end
describe "#error" do
it "adds and error" do
s = Source.new ""
s.error(DummyRule.new, 23, "Error!")
s.errors.size.should eq 1
s.errors.first.rule.should_not be_nil
s.errors.first.pos.should eq 23
s.errors.first.message.should eq "Error!"
s = Source.new "", "source.cr"
s.error(DummyRule.new, s.location(23, 2), "Error!")
s.should_not be_valid
error = s.errors.first
error.rule.should_not be_nil
error.location.to_s.should eq "source.cr:23:2"
error.message.should eq "Error!"
end
end
end

View file

@ -16,7 +16,7 @@ module Ameba
String.build do |str|
str << "Source expected to be valid, but there are errors:\n\n"
source.errors.each do |e|
str << " * #{e.rule.name}:#{e.pos} #{e.message}\n"
str << " * #{e.rule.name}: #{e.message}\n"
end
end
end

View file

@ -20,7 +20,7 @@ module Ameba::Formatter
failed_sources.each do |source|
source.errors.each do |error|
output << "#{source.path}:#{error.pos}\n".colorize(:cyan)
output << "#{error.location}\n".colorize(:cyan)
output << "#{error.rule.name}: #{error.message}\n\n".colorize(:red)
end
end

View file

@ -22,8 +22,7 @@ module Ameba::Rules
node.args.first?.try &.is_a?(Crystal::BoolLiteral) ||
node.obj.is_a?(Crystal::BoolLiteral)
)
source.error self, node.location.try &.line_number,
"Comparison to a boolean is pointless"
source.error self, node.location, "Comparison to a boolean is pointless"
end
end
end

View file

@ -25,7 +25,7 @@ module Ameba::Rules
name = target.names.first
return if (expected = name.upcase) == name
source.error self, node.location.try &.line_number,
source.error self, node.location,
"Constant name should be screaming-cased: #{expected}, not #{name}"
end
end

View file

@ -14,7 +14,7 @@ module Ameba::Rules
node.args.empty? &&
node.obj.nil?
source.error self, node.location.try &.line_number,
source.error self, node.location,
"Possible forgotten debugger statement detected"
end
end

View file

@ -33,8 +33,7 @@ module Ameba::Rules
return if exp.nil? || exp == "nil"
source.error self, node.location.try &.line_number,
"Avoid empty expression '#{exp}'"
source.error self, node.location, "Avoid empty expression '#{exp}'"
end
end
end

View file

@ -25,7 +25,7 @@ module Ameba::Rules
next unless token.type == :NUMBER && decimal?(token.raw)
if (expected = underscored token.raw) != token.raw
source.error self, token.line_number,
source.error self, token.location,
"Large numbers should be written with underscores: #{expected}"
end
end

View file

@ -5,8 +5,9 @@ module Ameba::Rules
def test(source)
source.lines.each_with_index do |line, index|
next unless line.size > 80
source.error self, index + 1,
"Line too long (#{line.size} symbols)"
source.error self, source.location(index + 1, line.size),
"Line too long"
end
end
end

View file

@ -22,8 +22,7 @@ module Ameba::Rules
def check_node(source, node)
return unless literal?(node.cond)
source.error self, node.location.try &.line_number,
"Literal value found in conditional"
source.error self, node.location, "Literal value found in conditional"
end
def test(source, node : Crystal::If)

View file

@ -19,8 +19,7 @@ module Ameba::Rules
def test(source, node : Crystal::StringInterpolation)
found = node.expressions.any? { |e| !string_literal?(e) && literal?(e) }
return unless found
source.error self, node.location.try &.line_number,
"Literal value found in interpolation"
source.error self, node.location, "Literal value found in interpolation"
end
end
end

View file

@ -38,7 +38,7 @@ module Ameba::Rules
def test(source, node : Crystal::Def)
return if (expected = node.name.underscore) == node.name
source.error self, node.location.try &.line_number,
source.error self, node.location,
"Method name should be underscore-cased: #{expected}, not #{node.name}"
end
end

View file

@ -27,7 +27,7 @@ module Ameba::Rules
def test(source, node : Crystal::Unless)
return unless negated_condition? node.cond
source.error self, node.location.try &.line_number,
source.error self, node.location,
"Avoid negated conditions in unless blocks"
end

View file

@ -29,7 +29,7 @@ module Ameba::Rules
def test(source, node : Crystal::Def)
if node.name =~ /(is|has)_(\w+)\?/
source.error self, node.location.try &.line_number,
source.error self, node.location,
"Favour method name '#{$2}?' over '#{node.name}'"
end
end

View file

@ -4,7 +4,7 @@ module Ameba::Rules
struct TrailingBlankLines < Rule
def test(source)
if source.lines.size > 1 && source.lines[-2, 2].join.strip.empty?
source.error self, source.lines.size,
source.error self, source.location(source.lines.size, 1),
"Blank lines detected at the end of the file"
end
end

View file

@ -5,7 +5,7 @@ module Ameba::Rules
def test(source)
source.lines.each_with_index do |line, index|
next unless line =~ /\s$/
source.error self, index + 1,
source.error self, source.location(index + 1, line.size),
"Trailing whitespace detected"
end
end

View file

@ -55,7 +55,7 @@ module Ameba::Rules
expected = name.camelcase
return if expected == name
source.error self, node.location.try &.line_number,
source.error self, node.location,
"Type name should be camelcased: #{expected}, but it was #{name}"
end

View file

@ -42,10 +42,8 @@ module Ameba::Rules
end
def test(source, node : Crystal::Unless)
unless node.else.is_a?(Crystal::Nop)
source.error self, node.location.try &.line_number,
"Favour if over unless with else"
end
return if node.else.nop?
source.error self, node.location, "Favour if over unless with else"
end
end
end

View file

@ -32,7 +32,7 @@ module Ameba::Rules
private def check_node(source, node)
return if (expected = node.name.underscore) == node.name
source.error self, node.location.try &.line_number,
source.error self, node.location,
"Var name should be underscore-cased: #{expected}, not #{node.name}"
end

View file

@ -8,20 +8,20 @@ module Ameba
# position of the error and a message.
record Error,
rule : Rule,
pos : Int32?,
location : Crystal::Location?,
message : String
getter lines : Array(String)?
getter errors = [] of Error
getter path : String?
getter content : String
getter code : String
getter ast : Crystal::ASTNode?
def initialize(@content : String, @path = nil)
def initialize(@code : String, @path = nil)
end
def error(rule : Rule, line_number : Int32?, message : String)
errors << Error.new rule, line_number, message
def error(rule : Rule, location, message : String)
errors << Error.new rule, location, message
end
def valid?
@ -29,14 +29,18 @@ module Ameba
end
def lines
@lines ||= @content.split("\n")
@lines ||= @code.split("\n")
end
def ast
@ast ||=
Crystal::Parser.new(content)
Crystal::Parser.new(code)
.tap { |parser| parser.filename = @path }
.parse
end
def location(l, c)
Crystal::Location.new path, l, c
end
end
end

View file

@ -3,7 +3,7 @@ require "compiler/crystal/syntax/*"
module Ameba
class Tokenizer
def initialize(source)
@lexer = Crystal::Lexer.new source.content
@lexer = Crystal::Lexer.new source.code
@lexer.count_whitespace = true
@lexer.comments_enabled = true
@lexer.wants_raw = true